CVE-2022-0869 (Python, published 2022-03-06T10:15Z)
Description: value: Multiple Open Redirect in GitHub repository nitely/spirit prior to 0.12.3.
Severity: MEDIUM (CVSS v3 base score: 6.1)
Weakness: CWE-601 \u2014 URL Redirection to Untrusted Site ('Open Redirect')
A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks.
Repository: https://github.com/nitely/spirit
Files changed:
spirit/admin/views.py
spirit/comment/flag/views.py
spirit/comment/like/views.py
spirit/comment/poll/views.py
spirit/comment/views.py
spirit/core/utils/decorators.py
spirit/core/utils/http.py
spirit/topic/favorite/views.py
spirit/topic/moderate/views.py
spirit/topic/notification/views.py
spirit/topic/private/views.py
spirit/topic/views.py
spirit/user/admin/views.py
spirit/user/auth/tests/tests.py
spirit/user/auth/views.py
Commit message: fix unsafe redirect (#308)



Vulnerable code:
from django.shortcuts import render, redirect
        return redirect(request.GET.get("next", request.get_full_path()))
from django.shortcuts import render, redirect, get_object_or_404
from ...core.utils.views import is_post, post_data
        return redirect(request.POST.get('next', comment.get_absolute_url()))
from django.shortcuts import render, redirect, get_object_or_404
        return redirect(request.POST.get('next', comment.get_absolute_url()))
        return redirect(request.POST.get('next', like.comment.get_absolute_url()))
from django.shortcuts import render, redirect, get_object_or_404
from ...core import utils
from ...core.utils.paginator import yt_paginate
    return redirect(request.GET.get('next', poll.get_absolute_url()))
        return redirect(request.POST.get('next', poll.get_absolute_url()))
    return redirect(request.POST.get('next', poll.get_absolute_url()))
            return redirect(
                request.POST.get('next', None) or
                Comment
        return redirect(request.POST.get('next', comment.get_absolute_url()))
        return redirect(request.POST.get('next', comment.get_absolute_url()))
        return redirect(request.GET.get('next', comment.get_absolute_url()))
    return redirect(request.POST.get('next', topic.get_absolute_url()))
from django.shortcuts import redirect
            return redirect(request.GET.get('next', request.user.st.get_absolute_url()))
from django.shortcuts import redirect
from ...core import utils
    return redirect(request.POST.get('next', topic.get_absolute_url()))
    return redirect(request.POST.get('next', favorite.topic.get_absolute_url()))
from django.shortcuts import render, redirect, get_object_or_404
        return redirect(request.POST.get(
            'next', topic.get_absolute_url()))
from django.shortcuts import render, redirect, get_object_or_404
    return redirect(request.POST.get('next', topic.get_absolute_url()))
    return redirect(request.POST.get(
        'next', notification.topic.get_absolute_url()))
    return redirect(request.POST.get(
        'next', reverse('spirit:topic:notification:index')))
from ...core.conf import settings
from ...core import utils
from ...core.utils.views import is_post, post_data
from ...core.utils.paginator import paginate, yt_paginate
from ...core.utils.ratelimit.decorators import ratelimit
from ...comment.forms import CommentForm
from ...comment.utils import comment_posted
from ...comment.models import Comment
            return redirect(
                request.POST.get('next', None) or
                tform.category.get_absolute_url())
    return redirect(request.POST.get('next', topic_private.get_absolute_url()))
        return redirect(request.POST.get('next', topic_private.get_absolute_url()))
        return redirect(request.POST.get('next', topic.get_absolute_url()))
            return redirect(
                request.POST.get('next', None) or
                form.get_category().get_absolute_url())
        return redirect(request.POST.get('next', topic.get_absolute_url()))
from django.shortcuts import render, redirect, get_object_or_404
from ...core.utils.views import is_post, post_data
from ...core.utils.paginator import yt_paginate
from ...core.utils.decorators import administrator_required
        return redirect(request.GET.get("next", request.get_full_path()))
        return redirect(request.GET.get('next', request.user.st.get_absolute_url()))
        return redirect(request.GET.get('next', reverse(settings.LOGIN_URL)))
        return redirect(request.GET.get('next', reverse('spirit:user:update')))
        # return redirect(request.GET.get('next', reverse('spirit:user:update')))
        return redirect(request.GET.get('next', reverse('spirit:user:update')))

Fixed code:
from django.shortcuts import render
from spirit.core.utils.http import safe_redirect
        return safe_redirect(request, "next", request.get_full_path())
from django.shortcuts import render, get_object_or_404
from spirit.core.utils.http import safe_redirect
from spirit.core.utils.views import is_post, post_data
        return safe_redirect(request, 'next', comment.get_absolute_url(), method='POST')
from django.shortcuts import render, get_object_or_404
from spirit.core.utils.http import safe_redirect
        return safe_redirect(request, 'next', comment.get_absolute_url(), method='POST')
        return safe_redirect(
            request, 'next', like.comment.get_absolute_url(), method='POST')
from django.shortcuts import render, get_object_or_404
from spirit.core.utils.http import safe_redirect
from spirit.core import utils
from spirit.core.utils.paginator import yt_paginate
    return safe_redirect(request, 'next', poll.get_absolute_url())
        return safe_redirect(request, 'next', poll.get_absolute_url(), method='POST')
    return safe_redirect(request, 'next', poll.get_absolute_url(), method='POST')
from spirit.core.utils.http import safe_redirect
            default_url = lambda: (Comment
            return safe_redirect(request, 'next', default_url, method='POST')
        return safe_redirect(request, 'next', comment.get_absolute_url(), method='POST')
        return safe_redirect(request, 'next', comment.get_absolute_url(), method='POST')
        return safe_redirect(request, 'next', comment.get_absolute_url())
    return safe_redirect(request, 'next', topic.get_absolute_url(), method='POST')
from spirit.core.utils.http import safe_redirect
            return safe_redirect(request, 'next', request.user.st.get_absolute_url())
# -*- coding: utf-8 -*-

from django.shortcuts import redirect
from django.utils.encoding import iri_to_uri

try:
    from django.utils.http import url_has_allowed_host_and_scheme
except ImportError:
    from django.utils.http import is_safe_url as url_has_allowed_host_and_scheme


def _resolve_lazy_url(url):
    if callable(url):
        return url()
    return url


def safe_redirect(request, key, default_url='', method='GET'):
    next = (
        getattr(request, method).get(key, None) or
        _resolve_lazy_url(default_url)
    )
    url_is_safe = url_has_allowed_host_and_scheme(
        url=next, allowed_hosts=None)
        #allowed_hosts=settings.ALLOWED_HOSTS,
        #require_https=request.is_secure())
    if url_is_safe:
        return redirect(iri_to_uri(next))
    return redirect('/')
from spirit.core import utils
from spirit.core.utils.http import safe_redirect
    return safe_redirect(request, 'next', topic.get_absolute_url(), method='POST')
    return safe_redirect(request, 'next', favorite.topic.get_absolute_url(), method='POST')
from django.shortcuts import render, get_object_or_404
from spirit.core.utils.http import safe_redirect
        return safe_redirect(request, 'next', topic.get_absolute_url(), method='POST')
from django.shortcuts import render, get_object_or_404
from spirit.core.utils.http import safe_redirect
    return safe_redirect(request, 'next', topic.get_absolute_url(), method='POST')
    return safe_redirect(
        request, 'next', notification.topic.get_absolute_url(), method='POST')
    return safe_redirect(
        request, 'next', reverse('spirit:topic:notification:index'), method='POST')
from spirit.core.conf import settings
from spirit.core import utils
from spirit.core.utils.http import safe_redirect
from spirit.core.utils.views import is_post, post_data
from spirit.core.utils.paginator import paginate, yt_paginate
from spirit.core.utils.ratelimit.decorators import ratelimit
from spirit.comment.forms import CommentForm
from spirit.comment.utils import comment_posted
from spirit.comment.models import Comment
            return safe_redirect(
                request, 'next', lambda: tform.category.get_absolute_url(), method='POST')
    return safe_redirect(
        request, 'next', topic_private.get_absolute_url(), method='POST')
        return safe_redirect(
            request, 'next', topic_private.get_absolute_url(), method='POST')
        return safe_redirect(
            request, 'next', topic.get_absolute_url(), method='POST')
from spirit.core.utils.http import safe_redirect
            default_url = lambda: form.get_category().get_absolute_url()
            return safe_redirect(
                request, 'next', default_url, method='POST')
        return safe_redirect(request,'next', topic.get_absolute_url(), method='POST')
from django.shortcuts import render, get_object_or_404
from spirit.core.utils.http import safe_redirect
from spirit.core.utils.views import is_post, post_data
from spirit.core.utils.paginator import yt_paginate
from spirit.core.utils.decorators import administrator_required
        return safe_redirect(request, "next", request.get_full_path())
    def test_login_open_redirect(self):
        utils.login(self)
        response = self.client.get(reverse('spirit:user:auth:login') + '?next=https%3A%2F%2Fevil.com')
        self.assertRedirects(response, '/', status_code=302)

from spirit.core.utils.http import safe_redirect
        return safe_redirect(
            request, 'next', request.user.st.get_absolute_url())
        return safe_redirect(request, 'next', reverse(settings.LOGIN_URL))
        return safe_redirect(request, 'next', reverse('spirit:user:update'))
        # return safe_redirect(request, 'next', reverse('spirit:user:update'))
        return safe_redirect(request, 'next', reverse('spirit:user:update'))