CVE-2022-0697: URL Redirection to Untrusted Site ('Open Redirect') (CWE-601)
value: Open Redirect in GitHub repository archivy/archivy prior to 1.7.0.
CWE description: 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.
CVSS v3 base score: 6.1
Published: 2022-03-06T23:15Z
Repository: https://github.com/archivy/archivy
Language: Python
Files changed: archivy/helpers.py, archivy/routes.py
Commit date: 2022-02-24 21:35:55 +0100
Commit message: fix open redirect

Diff: diff --git a/archivy/helpers.py b/archivy/helpers.py
index df6d63c..1eddbdd 100644
--- a/archivy/helpers.py
+++ b/archivy/helpers.py
@@ -3,12 +3,13 @@ import sys
 import os
 
 import elasticsearch
 import yaml
 from elasticsearch import Elasticsearch
-from flask import current_app, g
+from flask import current_app, g, request
 from tinydb import TinyDB, Query, operations
+from urllib.parse import urlparse, urljoin
 
 from archivy.config import BaseHooks, Config
 
 
 def load_config(path=""):
@@ -228,5 +229,14 @@ def create_plugin_dir(name):
             )
 
         return True
     except FileExistsError:
         return False
+
+
+def is_safe_redirect_url(target):
+    host_url = urlparse(request.host_url)
+    redirect_url = urlparse(urljoin(request.host_url, target))
+    return (
+        redirect_url.scheme in ("http", "https")
+        and host_url.netloc == redirect_url.netloc
+    )
diff --git a/archivy/routes.py b/archivy/routes.py
index f96ff51..173e2b1 100644
--- a/archivy/routes.py
+++ b/archivy/routes.py
@@ -17,11 +17,11 @@ from flask_login import login_user, current_user, logout_user
 from tinydb import Query
 from werkzeug.security import check_password_hash, generate_password_hash
 
 from archivy.models import DataObj, User
 from archivy import data, app, forms, csrf
-from archivy.helpers import get_db, write_config
+from archivy.helpers import get_db, write_config, is_safe_redirect_url
 from archivy.tags import get_all_tags
 from archivy.search import search, search_frontmatter_tags
 from archivy.config import Config
 
 import re
@@ -262,11 +262,14 @@ def login():
             user = User.from_db(user[0])
             login_user(user, remember=True)
             flash("Login successful!", "success")
 
             next_url = request.args.get("next")
-            return redirect(next_url or "/")
+            if next_url and is_safe_redirect_url(next_url):
+                return redirect(next_url)
+            else:
+                return redirect("/")
 
         flash("Invalid credentials", "error")
         return redirect("/login")
     return render_template("users/login.html", form=form, title="Login")
 

Vulnerable code: from flask import current_app, g
from archivy.helpers import get_db, write_config
            return redirect(next_url or "/")
Fixed code: from flask import current_app, g, request
from urllib.parse import urlparse, urljoin


def is_safe_redirect_url(target):
    host_url = urlparse(request.host_url)
    redirect_url = urlparse(urljoin(request.host_url, target))
    return (
        redirect_url.scheme in ("http", "https")
        and host_url.netloc == redirect_url.netloc
    )
from archivy.helpers import get_db, write_config, is_safe_redirect_url
            if next_url and is_safe_redirect_url(next_url):
                return redirect(next_url)
            else:
                return redirect("/")