git: a2d439758b88 - 2025Q3 - devel/viewvc-devel: Patch to vix known vuln

From: Dan Langille <dvl_at_FreeBSD.org>
Date: Wed, 23 Jul 2025 00:19:56 UTC
The branch 2025Q3 has been updated by dvl:

URL: https://cgit.FreeBSD.org/ports/commit/?id=a2d439758b886851bc5c1cc0950509f9b9d7aa5e

commit a2d439758b886851bc5c1cc0950509f9b9d7aa5e
Author:     Dan Langille <dvl@FreeBSD.org>
AuthorDate: 2025-07-22 22:14:45 +0000
Commit:     Dan Langille <dvl@FreeBSD.org>
CommitDate: 2025-07-23 00:14:53 +0000

    devel/viewvc-devel: Patch to vix known vuln
    
    Cherry pick that one commit:
    
    https://github.com/viewvc/viewvc/commit/beef140e4102c143185bce31b9a50f5a35d1c75c
    
    PR:             288388
    (cherry picked from commit aa6dff4fc5a2af00e680a4158040f475efbb58df)
---
 devel/viewvc-devel/Makefile                      |  1 +
 devel/viewvc-devel/files/patch-bin_standalone.py | 74 ++++++++++++++++++++++++
 devel/viewvc-devel/files/patch-lib_viewvc.py     | 39 +++++++++++++
 3 files changed, 114 insertions(+)

diff --git a/devel/viewvc-devel/Makefile b/devel/viewvc-devel/Makefile
index 658733568732..96e7f66c20a4 100644
--- a/devel/viewvc-devel/Makefile
+++ b/devel/viewvc-devel/Makefile
@@ -1,5 +1,6 @@
 PORTNAME=	viewvc
 DISTVERSION=	1.3.0-20250316
+PORTREVISION=	1
 CATEGORIES=	devel python
 PKGNAMEPREFIX=	${PYTHON_PKGNAMEPREFIX}
 PKGNAMESUFFIX=	-devel
diff --git a/devel/viewvc-devel/files/patch-bin_standalone.py b/devel/viewvc-devel/files/patch-bin_standalone.py
new file mode 100644
index 000000000000..4397053b7391
--- /dev/null
+++ b/devel/viewvc-devel/files/patch-bin_standalone.py
@@ -0,0 +1,74 @@
+--- bin/standalone.py.orig	2025-07-22 12:12:06 UTC
++++ bin/standalone.py
+@@ -191,18 +191,17 @@ class ViewVCHTTPRequestHandler(_http_server.BaseHTTPRe
+ </html>"""
+             )
+ 
+-    def is_viewvc(self):
++    def is_viewvc(self, path):
+         """Check whether self.path is, or is a child of, the ScriptAlias"""
++        if not path.startswith("/"):
++            return False
+         if not options.script_alias:
+-            return 1
+-        if self.path == "/" + options.script_alias:
+-            return 1
+-        alias_len = len(options.script_alias)
+-        if self.path[: (alias_len + 2)] == "/" + options.script_alias + "/":
+-            return 1
+-        if self.path[: (alias_len + 2)] == "/" + options.script_alias + "?":
+-            return 1
+-        return 0
++            return True
++        if path == "/" + options.script_alias:
++            return True
++        if path.startswith("/" + options.script_alias + "/"):
++            return True
++        return False
+ 
+     def validate_password(self, htpasswd_file, username, password):
+         """Compare USERNAME and PASSWORD against HTPASSWD_FILE."""
+@@ -219,8 +218,18 @@ class ViewVCHTTPRequestHandler(_http_server.BaseHTTPRe
+         # NOTE: Much of this is adapter from Python's standard library
+         # module CGIHTTPServer.
+ 
++        i = self.path.rfind("?")
++        if i >= 0:
++            path = _unquote(self.path[:i], "utf-8", "surrogateescape")
++            query = self.path[(i + 1) :]
++        else:
++            path = _unquote(self.path)
++            query = ""
++        # normalize path
++        path = os.path.normpath(path) + ("/" if path[-1] == "/" else "")
++
+         # Is this request even aimed at ViewVC?  If not, complain.
+-        if not self.is_viewvc():
++        if not self.is_viewvc(path):
+             raise NotViewVCLocationException()
+ 
+         # If htpasswd authentication is enabled, try to authenticate the user.
+@@ -245,12 +254,7 @@ class ViewVCHTTPRequestHandler(_http_server.BaseHTTPRe
+ 
+         scriptname = options.script_alias and "/" + options.script_alias or ""
+ 
+-        rest = self.path[len(scriptname) :]
+-        i = rest.rfind("?")
+-        if i >= 0:
+-            rest, query = rest[:i], rest[(i + 1) :]
+-        else:
+-            query = ""
++        rest = path[len(scriptname) :]
+ 
+         # Since we're going to modify the env in the parent, provide empty
+         # values to override previously set values
+@@ -274,8 +278,7 @@ class ViewVCHTTPRequestHandler(_http_server.BaseHTTPRe
+         env["SERVER_PROTOCOL"] = self.protocol_version
+         env["SERVER_PORT"] = str(self.server.server_port)
+         env["REQUEST_METHOD"] = self.command
+-        uqrest = _unquote(rest, "utf-8", "surrogateescape")
+-        env["PATH_INFO"] = uqrest
++        env["PATH_INFO"] = rest
+         env["SCRIPT_NAME"] = scriptname
+         if query:
+             env["QUERY_STRING"] = query
diff --git a/devel/viewvc-devel/files/patch-lib_viewvc.py b/devel/viewvc-devel/files/patch-lib_viewvc.py
new file mode 100644
index 000000000000..739d60e1a50a
--- /dev/null
+++ b/devel/viewvc-devel/files/patch-lib_viewvc.py
@@ -0,0 +1,39 @@
+--- lib/viewvc.py.orig	2025-07-22 12:12:06 UTC
++++ lib/viewvc.py
+@@ -193,6 +193,10 @@ class Request:
+         # TODO: we might want to redirect to the cleaned up URL
+         path_parts = _path_parts(path_info)
+ 
++        # Protect against directory traversal attacks.
++        if ".." in path_parts:
++            raise ViewVCException("An illegal path was provided.", "400 Bad Request")
++
+         if path_parts:
+             # handle docroot magic path prefixes
+             if path_parts[0] == docroot_magic_path:
+@@ -3401,10 +3405,8 @@ def view_doc(request):
+     # Stat the file to get content length and last-modified date.
+     try:
+         info = os.stat(filename)
+-    except OSError as v:
+-        raise ViewVCException(
+-            'Static file "%s" not available (%s)' % (document, str(v)), "404 Not Found"
+-        )
++    except OSError:
++        raise ViewVCException('Static file "%s" not available' % (document), "404 Not Found")
+     content_length = str(info[stat.ST_SIZE])
+     last_modified = info[stat.ST_MTIME]
+ 
+@@ -3414,10 +3416,8 @@ def view_doc(request):
+ 
+     try:
+         fp = open(filename, "rb")
+-    except IOError as v:
+-        raise ViewVCException(
+-            'Static file "%s" not available (%s)' % (document, str(v)), "404 Not Found"
+-        )
++    except IOError:
++        raise ViewVCException('Static file "%s" not available' % (document), "404 Not Found")
+ 
+     if document[-3:] == "png":
+         mime_type = "image/png"