Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-devpi-server for 
openSUSE:Factory checked in at 2026-11-09 16:04:34
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-devpi-server (Old)
 and      /work/SRC/openSUSE:Factory/.python-devpi-server.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-devpi-server"

Mon Nov  9 16:04:34 2026 rev:24 rq:1396187 version:6.21.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-devpi-server/python-devpi-server.changes  
2026-10-13 17:52:48.145064278 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-devpi-server.new.3759290/python-devpi-server.changes
     2026-11-09 16:05:18.651490862 +0100
@@ -1,0 +2,11 @@
+Sun Nov  8 21:07:38 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 6.21.0:
+  * Use sha256 for login token signing. Existing tokens will be
+    invalidated, but they are valid for only 10 hours anyway.
+  * Block ``.`` and ``..`` as user and index names.
+  * Reported by Zachariah Mithani [zachariah-mithani] (AploSec)
+  * https://github.com/devpi/devpi/security/advisories/GHSA-w72q-
+    5vww-p2rh
+
+-------------------------------------------------------------------

Old:
----
  devpi_server-6.20.3.tar.gz

New:
----
  devpi_server-6.21.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-devpi-server.spec ++++++
--- /var/tmp/diff_new_pack.tPwYS4/_old  2026-11-09 16:05:19.385521634 +0100
+++ /var/tmp/diff_new_pack.tPwYS4/_new  2026-11-09 16:05:19.387521718 +0100
@@ -26,7 +26,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-devpi-server
-Version:        6.20.3
+Version:        6.21.0
 Release:        0
 Summary:        Private PyPI caching server
 License:        MIT

++++++ devpi_server-6.20.3.tar.gz -> devpi_server-6.21.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/AUTHORS 
new/devpi_server-6.21.0/AUTHORS
--- old/devpi_server-6.20.3/AUTHORS     1970-01-01 01:00:00.000000000 +0100
+++ new/devpi_server-6.21.0/AUTHORS     2016-09-12 12:26:20.000000000 +0200
@@ -0,0 +1,13 @@
+Authors:
+
+Holger Krekel
+Florian Schulze
+
+Contributors:
+
+Anthon van der Neut
+Marc Abramowitz
+lbrack
+holger
+Edward Easton
+Christian Ullrich
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/CHANGELOG 
new/devpi_server-6.21.0/CHANGELOG
--- old/devpi_server-6.20.3/CHANGELOG   2026-06-30 18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/CHANGELOG   2026-10-26 09:49:31.595840200 +0100
@@ -2,6 +2,24 @@
 
 .. towncrier release notes start
 
+6.21.0 (2026-10-26)
+===================
+
+Features
+--------
+
+- Use sha256 for login token signing. Existing tokens will be invalidated, but 
they are valid for only 10 hours anyway.
+
+Bug Fixes
+---------
+
+- Block ``.`` and ``..`` as user and index names.
+  Reported by Zachariah Mithani [zachariah-mithani] (AploSec)
+  https://github.com/devpi/devpi/security/advisories/GHSA-w72q-5vww-p2rh
+
+- Fix #1130: correctly compare content type header to ignore added parameters 
for API v1 JSON responses.
+
+
 6.20.3 (2026-06-30)
 ===================
 
@@ -20,6 +38,8 @@
 ---------
 
 - Fix logic error which allowed unauthorized fetching of replication data.
+  CVE-2026-54723 reported by Pavel Revak [pavelrevak]
+  https://github.com/devpi/devpi/security/advisories/GHSA-m5pq-69xg-vcq3
 
 
 6.20.1 (2026-05-11)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/CHANGELOG.short.rst 
new/devpi_server-6.21.0/CHANGELOG.short.rst
--- old/devpi_server-6.20.3/CHANGELOG.short.rst 2026-06-30 18:37:42.000000000 
+0200
+++ new/devpi_server-6.21.0/CHANGELOG.short.rst 2026-10-26 09:54:21.120204400 
+0100
@@ -9,6 +9,24 @@
 
 .. towncrier release notes start
 
+6.21.0 (2026-10-26)
+===================
+
+Features
+--------
+
+- Use sha256 for login token signing. Existing tokens will be invalidated, but 
they are valid for only 10 hours anyway.
+
+Bug Fixes
+---------
+
+- Block ``.`` and ``..`` as user and index names.
+  Reported by Zachariah Mithani [zachariah-mithani] (AploSec)
+  https://github.com/devpi/devpi/security/advisories/GHSA-w72q-5vww-p2rh
+
+- Fix #1130: correctly compare content type header to ignore added parameters 
for API v1 JSON responses.
+
+
 6.20.3 (2026-06-30)
 ===================
 
@@ -27,6 +45,8 @@
 ---------
 
 - Fix logic error which allowed unauthorized fetching of replication data.
+  CVE-2026-54723 reported by Pavel Revak [pavelrevak]
+  https://github.com/devpi/devpi/security/advisories/GHSA-m5pq-69xg-vcq3
 
 
 6.20.1 (2026-05-11)
@@ -51,16 +71,3 @@
 
 - Update replica status when the replica is waiting for new serials using the 
streaming changelog endpoint.
 
-
-6.19.3 (2026-04-13)
-===================
-
-Bug Fixes
----------
-
-- Fix #1112: Parse simple JSON reply even with wrong content-type in reply if 
the body seems to contain JSON.
-
-- Return stale project list for mirrors when the lock can't be acquired within 
the timeout.
-
-- Fix importing of toxresults from devpi-server 6.5.0 to 6.9.0 where the wrong 
hash was stored.
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/PKG-INFO 
new/devpi_server-6.21.0/PKG-INFO
--- old/devpi_server-6.20.3/PKG-INFO    2026-06-30 18:37:42.541430500 +0200
+++ new/devpi_server-6.21.0/PKG-INFO    2026-10-26 09:54:21.212840600 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: devpi-server
-Version: 6.20.3
+Version: 6.21.0
 Summary: devpi-server: backend for hosting private package indexes and PyPI 
on-demand mirrors
 Maintainer-email: Florian Schulze <[email protected]>
 License-Expression: MIT
@@ -121,6 +121,24 @@
 
 .. towncrier release notes start
 
+6.21.0 (2026-10-26)
+===================
+
+Features
+--------
+
+- Use sha256 for login token signing. Existing tokens will be invalidated, but 
they are valid for only 10 hours anyway.
+
+Bug Fixes
+---------
+
+- Block ``.`` and ``..`` as user and index names.
+  Reported by Zachariah Mithani [zachariah-mithani] (AploSec)
+  https://github.com/devpi/devpi/security/advisories/GHSA-w72q-5vww-p2rh
+
+- Fix #1130: correctly compare content type header to ignore added parameters 
for API v1 JSON responses.
+
+
 6.20.3 (2026-06-30)
 ===================
 
@@ -139,6 +157,8 @@
 ---------
 
 - Fix logic error which allowed unauthorized fetching of replication data.
+  CVE-2026-54723 reported by Pavel Revak [pavelrevak]
+  https://github.com/devpi/devpi/security/advisories/GHSA-m5pq-69xg-vcq3
 
 
 6.20.1 (2026-05-11)
@@ -163,16 +183,3 @@
 
 - Update replica status when the replica is waiting for new serials using the 
streaming changelog endpoint.
 
-
-6.19.3 (2026-04-13)
-===================
-
-Bug Fixes
----------
-
-- Fix #1112: Parse simple JSON reply even with wrong content-type in reply if 
the body seems to contain JSON.
-
-- Return stale project list for mirrors when the lock can't be acquired within 
the timeout.
-
-- Fix importing of toxresults from devpi-server 6.5.0 to 6.9.0 where the wrong 
hash was stored.
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/devpi_server/__init__.py 
new/devpi_server-6.21.0/devpi_server/__init__.py
--- old/devpi_server-6.20.3/devpi_server/__init__.py    2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server/__init__.py    2026-10-26 
09:37:11.732264500 +0100
@@ -1 +1 @@
-__version__ = "6.20.3"
+__version__ = "6.21.0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/devpi_server/auth.py 
new/devpi_server-6.21.0/devpi_server/auth.py
--- old/devpi_server-6.20.3/devpi_server/auth.py        2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server/auth.py        2026-10-25 
08:08:59.759964700 +0100
@@ -2,6 +2,7 @@
 
 from .log import threadlog
 from .markers import notset
+from hashlib import sha256
 from passlib.context import CryptContext
 from typing import TYPE_CHECKING
 import itertools
@@ -24,7 +25,9 @@
 
     def __init__(self, xom: XOM, secret: str) -> None:
         self.xom = xom
-        self.serializer = itsdangerous.TimedSerializer(secret)
+        self.serializer = itsdangerous.TimedSerializer(
+            secret, signer_kwargs={"digest_method": sha256}
+        )
         self.hook = xom.config.hook.devpiserver_auth_request
         self.legacy_hook = xom.config.hook.devpiserver_auth_user
         self.autocreate_users = xom.config.autocreate_users
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/devpi_server/fileutil.py 
new/devpi_server-6.21.0/devpi_server/fileutil.py
--- old/devpi_server-6.20.3/devpi_server/fileutil.py    2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server/fileutil.py    2026-10-25 
10:25:17.824929000 +0100
@@ -2,6 +2,7 @@
 
 from . import filestore
 from . import readonly
+from .keyfs_types import RelPath
 from .markers import nodefault as _nodefault
 from .normalized import NormalizedName
 from io import BytesIO
@@ -227,6 +228,7 @@
     readonly.SetViewReadonly: _dump_set,
     complex: _dump_complex,
     NormalizedName: _dump_str,
+    RelPath: _dump_str,
 }
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/devpi_server/httpclient.py 
new/devpi_server-6.21.0/devpi_server/httpclient.py
--- old/devpi_server-6.20.3/devpi_server/httpclient.py  2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server/httpclient.py  2026-10-25 
08:08:59.761881000 +0100
@@ -88,7 +88,7 @@
     @cached_property
     def _ssl_context(self) -> ssl.SSLContext:
         # create an SSLContext object that uses the same CA certs as requests
-        cafile = (
+        cafile: str | None = (
             os.environ.get("REQUESTS_CA_BUNDLE")
             or os.environ.get("CURL_CA_BUNDLE")
             or DEFAULT_CA_BUNDLE_PATH
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/devpi_server/keyfs_types.py 
new/devpi_server-6.21.0/devpi_server/keyfs_types.py
--- old/devpi_server-6.20.3/devpi_server/keyfs_types.py 2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server/keyfs_types.py 2026-10-25 
10:40:30.277811300 +0100
@@ -7,12 +7,12 @@
 from attrs import frozen
 from collections.abc import Hashable
 from typing import Generic
-from typing import NewType
 from typing import TYPE_CHECKING
 from typing import TypeVar
 from typing import Union
 from typing import overload
 import contextlib
+import os.path
 import re
 import warnings
 
@@ -28,6 +28,7 @@
     from collections.abc import Iterator
     from typing import Any
     from typing import Literal
+    from typing_extensions import Self
 
     KeyFSTypesRO = Union[
         bool,
@@ -66,7 +67,23 @@
                 raise TypeError(msg)
 
 
-RelPath = NewType("RelPath", str)
+class InvalidRelPathError(ValueError):
+    pass
+
+
+class RelPath(str):
+    __slots__ = ()
+
+    def __new__(cls, relpath: Self | str) -> Self:
+        if os.path.isabs(relpath):
+            raise InvalidRelPathError("RelPath must be relative")
+        if {".", ".."}.intersection(os.path.split(relpath)):
+            raise InvalidRelPathError("RelPath must not contain '.' or '..'")
+        return super().__new__(cls, relpath)
+
+    def __repr__(self) -> str:
+        orig = super().__repr__()
+        return f"<{self.__class__.__name__} {orig!r}>"
 
 
 @define
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/devpi_server/mirror.py 
new/devpi_server-6.21.0/devpi_server/mirror.py
--- old/devpi_server-6.20.3/devpi_server/mirror.py      2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server/mirror.py      2026-10-25 
08:08:59.763521000 +0100
@@ -29,6 +29,7 @@
 from devpi_common.metadata import parse_version
 from devpi_common.types import cached_property
 from devpi_common.url import URL
+from email.message import Message
 from functools import partial
 from html.parser import HTMLParser
 from pyramid.authentication import b64encode
@@ -47,6 +48,7 @@
 if TYPE_CHECKING:
     from .filestore import FileEntry
     from .httpclient import AsyncGetResponse
+    from .httpclient import GetResponse
     from .httpclient import HTTPClient
     from .keyfs_types import PTypedKey
     from .main import XOM
@@ -96,6 +98,13 @@
     "text/html;q=0.01"))
 
 
+def is_json_v1_api(response: GetResponse, text: str) -> bool:
+    m = Message()
+    m["content-type"] = response.headers.get("content-type", 
"application/octet-stream")
+    content_type = m.get_content_type()
+    return content_type == SIMPLE_API_V1_JSON or text.startswith("{")
+
+
 class Link(URL):
     def __init__(self, url="", *args, **kwargs):
         self.requires_python = kwargs.pop('requires_python', None)
@@ -666,9 +675,7 @@
             )
         assert text is not None
         parser: ProjectHTMLParser | ProjectJSONv1Parser
-        if (
-            response.headers.get("content-type") == SIMPLE_API_V1_JSON
-        ) or text.startswith("{"):
+        if is_json_v1_api(response, text):
             parser = ProjectJSONv1Parser(response.url)
             parser.feed(json.loads(text))
         else:
@@ -919,7 +926,7 @@
         # make sure we don't store credential in the database
         response_url = URL(str(response.url)).replace(username=None, 
password=None)
         # parse simple index's link
-        if response.headers.get('content-type') == SIMPLE_API_V1_JSON:
+        if is_json_v1_api(response, text):
             releaselinks = parse_index_v1_json(response_url, text)
         else:
             releaselinks = parse_index(response_url, text).releaselinks
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/devpi_server/model.py 
new/devpi_server-6.21.0/devpi_server/model.py
--- old/devpi_server-6.20.3/devpi_server/model.py       2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server/model.py       2026-10-25 
10:41:17.706956600 +0100
@@ -7,6 +7,7 @@
 from .filestore import Digests
 from .filestore import FileEntry
 from .filestore import get_hash_spec
+from .keyfs_types import InvalidRelPathError
 from .log import threadlog
 from .markers import NotSet
 from .markers import notset
@@ -16,6 +17,7 @@
 from .readonly import SetViewReadonly
 from .readonly import get_mutable_deepcopy
 from abc import abstractmethod
+from contextlib import suppress
 from devpi_common.metadata import get_latest_version
 from devpi_common.metadata import parse_version
 from devpi_common.metadata import splitbasename
@@ -241,8 +243,9 @@
 
     def get_user(self, name: str) -> User | None:
         user = User(self, name)
-        if user.key.exists():
-            return user
+        with suppress(InvalidRelPathError):
+            if user.key.exists():
+                return user
         return None
 
     def get_userlist(self):
@@ -347,7 +350,7 @@
 
 
 def is_valid_name(name: str) -> bool:
-    return not name_char_blocklist_regexp.search(name)
+    return not (name in {".", ".."} or name_char_blocklist_regexp.search(name))
 
 
 class InvalidUserconfig(Exception):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/devpi_server/sizeof.py 
new/devpi_server-6.21.0/devpi_server/sizeof.py
--- old/devpi_server-6.20.3/devpi_server/sizeof.py      2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server/sizeof.py      2026-10-25 
08:08:59.764773600 +0100
@@ -51,6 +51,4 @@
         return result
 except TypeError:
     # PyPy doesn't implement getsizeof, use dumplen as estimate
-    from .fileutil import (  # type: ignore[assignment] # noqa: F401
-        dumplen as gettotalsizeof,
-    )
+    from .fileutil import dumplen as gettotalsizeof  # type: 
ignore[assignment] # noqa: F401
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/devpi_server.egg-info/PKG-INFO 
new/devpi_server-6.21.0/devpi_server.egg-info/PKG-INFO
--- old/devpi_server-6.20.3/devpi_server.egg-info/PKG-INFO      2026-06-30 
18:37:42.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server.egg-info/PKG-INFO      2026-10-26 
09:54:21.134183000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: devpi-server
-Version: 6.20.3
+Version: 6.21.0
 Summary: devpi-server: backend for hosting private package indexes and PyPI 
on-demand mirrors
 Maintainer-email: Florian Schulze <[email protected]>
 License-Expression: MIT
@@ -121,6 +121,24 @@
 
 .. towncrier release notes start
 
+6.21.0 (2026-10-26)
+===================
+
+Features
+--------
+
+- Use sha256 for login token signing. Existing tokens will be invalidated, but 
they are valid for only 10 hours anyway.
+
+Bug Fixes
+---------
+
+- Block ``.`` and ``..`` as user and index names.
+  Reported by Zachariah Mithani [zachariah-mithani] (AploSec)
+  https://github.com/devpi/devpi/security/advisories/GHSA-w72q-5vww-p2rh
+
+- Fix #1130: correctly compare content type header to ignore added parameters 
for API v1 JSON responses.
+
+
 6.20.3 (2026-06-30)
 ===================
 
@@ -139,6 +157,8 @@
 ---------
 
 - Fix logic error which allowed unauthorized fetching of replication data.
+  CVE-2026-54723 reported by Pavel Revak [pavelrevak]
+  https://github.com/devpi/devpi/security/advisories/GHSA-m5pq-69xg-vcq3
 
 
 6.20.1 (2026-05-11)
@@ -163,16 +183,3 @@
 
 - Update replica status when the replica is waiting for new serials using the 
streaming changelog endpoint.
 
-
-6.19.3 (2026-04-13)
-===================
-
-Bug Fixes
----------
-
-- Fix #1112: Parse simple JSON reply even with wrong content-type in reply if 
the body seems to contain JSON.
-
-- Return stale project list for mirrors when the lock can't be acquired within 
the timeout.
-
-- Fix importing of toxresults from devpi-server 6.5.0 to 6.9.0 where the wrong 
hash was stored.
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/devpi_server-6.20.3/devpi_server.egg-info/SOURCES.txt 
new/devpi_server-6.21.0/devpi_server.egg-info/SOURCES.txt
--- old/devpi_server-6.20.3/devpi_server.egg-info/SOURCES.txt   2026-06-30 
18:37:42.000000000 +0200
+++ new/devpi_server-6.21.0/devpi_server.egg-info/SOURCES.txt   2026-10-26 
09:54:21.176675600 +0100
@@ -1,4 +1,5 @@
 .flake8
+AUTHORS
 CHANGELOG
 CHANGELOG.short.rst
 LICENSE
@@ -87,6 +88,7 @@
 test_devpi_server/test_genconfig.py
 test_devpi_server/test_importexport.py
 test_devpi_server/test_keyfs.py
+test_devpi_server/test_keyfs_types.py
 test_devpi_server/test_log.py
 test_devpi_server/test_main.py
 test_devpi_server/test_mirror.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/pyproject.toml 
new/devpi_server-6.21.0/pyproject.toml
--- old/devpi_server-6.20.3/pyproject.toml      2026-06-30 18:36:41.000000000 
+0200
+++ new/devpi_server-6.21.0/pyproject.toml      2026-10-25 08:08:59.765459000 
+0100
@@ -184,6 +184,7 @@
     "FIX003", # cleanup later - xxx
     "FLY002", # maybe cleanup later - f-string instead of join
     "FURB110", # maybe cleanup later - or instead of ternary
+    "ICN001", # datetime as dt
     "PERF401", # maybe cleanup later - list comprehension
     "PLR0911", # cleanup later
     "PLR0912", # maybe cleanup later
@@ -201,6 +202,7 @@
     "RET507", # maybe cleanup later - unnecessary elif after continue
     "RUF005", # cleanup later
     "RUF012", # cleanup later
+    "RUF036", # cleanup later - None not at the end of the type union
     "RUF043", # cleanup later - pytest.raises match regexp
     "RUF059", # cleanup later - unpacked variable unused
     "RSE102", # maybe cleanup later
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/devpi_server-6.20.3/test_devpi_server/test_importexport.py 
new/devpi_server-6.21.0/test_devpi_server/test_importexport.py
--- old/devpi_server-6.20.3/test_devpi_server/test_importexport.py      
2026-06-30 18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/test_devpi_server/test_importexport.py      
2026-10-25 08:08:59.767216400 +0100
@@ -272,7 +272,8 @@
                     *options,
                     self.exportdir]
                 pm = get_pluginmanager()
-                pm.register(plugin)
+                if plugin is not None:
+                    pm.register(plugin)
                 assert import_(pluginmanager=pm, argv=argv) == 0
                 mapp2 = makemapp(options=["--serverdir", serverdir])
                 if plugin is not None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/devpi_server-6.20.3/test_devpi_server/test_keyfs_types.py 
new/devpi_server-6.21.0/test_devpi_server/test_keyfs_types.py
--- old/devpi_server-6.20.3/test_devpi_server/test_keyfs_types.py       
1970-01-01 01:00:00.000000000 +0100
+++ new/devpi_server-6.21.0/test_devpi_server/test_keyfs_types.py       
2026-10-25 10:20:29.866234800 +0100
@@ -0,0 +1,16 @@
+from __future__ import annotations
+
+from devpi_server.keyfs_types import RelPath
+import pytest
+
+
[email protected](("relpath", "match_str"), [
+    ("../foo", "must not contain"),
+    ("./foo", "must not contain"),
+    ("/absolute", "must be relative"),
+    ("foo/.", "must not contain"),
+    ("foo/..", "must not contain"),
+])
+def test_invalid_relpath(match_str: str, relpath: str) -> None:
+    with pytest.raises(ValueError, match=match_str):
+        RelPath(relpath)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/test_devpi_server/test_mirror.py 
new/devpi_server-6.21.0/test_devpi_server/test_mirror.py
--- old/devpi_server-6.20.3/test_devpi_server/test_mirror.py    2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/test_devpi_server/test_mirror.py    2026-10-25 
08:08:59.767833000 +0100
@@ -268,12 +268,20 @@
 
 
 class TestExtPYPIDB:
-    def test_parse_pep691(self, pypistage):
+    @pytest.mark.parametrize(
+        "content_type",
+        [
+            "application/vnd.pypi.simple.v1+json; api-version=7.2-preview.1",
+            "application/vnd.pypi.simple.v1+json",
+            "application/octet-stream",
+        ],
+    )
+    def test_parse_pep691(self, content_type, pypistage):
         pypistage.mock_simple_projects(["devpi"])
         pypistage.xom.http.mockresponse(
             URL(pypistage.mirror_url).joinpath("devpi").asdir().url,
             code=200,
-            content_type="application/vnd.pypi.simple.v1+json",
+            content_type=content_type,
             text="""{
                 "meta": {"api-version": "1.0"},
                 "name": "devpi",
@@ -294,12 +302,20 @@
         assert link.yanked is None
         assert link.require_python is None
 
-    def test_parse_pep691_data(self, pypistage):
+    @pytest.mark.parametrize(
+        "content_type",
+        [
+            "application/vnd.pypi.simple.v1+json; api-version=7.2-preview.1",
+            "application/vnd.pypi.simple.v1+json",
+            "application/octet-stream",
+        ],
+    )
+    def test_parse_pep691_data(self, content_type, pypistage):
         pypistage.mock_simple_projects(["devpi"])
         pypistage.xom.http.mockresponse(
             URL(pypistage.mirror_url).joinpath("devpi").asdir().url,
             code=200,
-            content_type="application/vnd.pypi.simple.v1+json",
+            content_type=content_type,
             text="""{
                 "meta": {"api-version": "1.0"},
                 "name": "devpi",
@@ -321,12 +337,20 @@
         assert link.yanked == "brownbag"
         assert link.require_python == ">=3.6"
 
-    def test_parse_pep691_md5(self, pypistage):
+    @pytest.mark.parametrize(
+        "content_type",
+        [
+            "application/vnd.pypi.simple.v1+json; api-version=7.2-preview.1",
+            "application/vnd.pypi.simple.v1+json",
+            "application/octet-stream",
+        ],
+    )
+    def test_parse_pep691_md5(self, content_type, pypistage):
         pypistage.mock_simple_projects(["devpi"])
         pypistage.xom.http.mockresponse(
             URL(pypistage.mirror_url).joinpath("devpi").asdir().url,
             code=200,
-            content_type="application/vnd.pypi.simple.v1+json",
+            content_type=content_type,
             text="""{
                 "meta": {"api-version": "1.0"},
                 "name": "devpi",
@@ -897,7 +921,11 @@
     @pytest.mark.asyncio
     @pytest.mark.parametrize(
         "content_type",
-        ["application/vnd.pypi.simple.v1+json", "application/octet-stream"],
+        [
+            "application/vnd.pypi.simple.v1+json; api-version=7.2-preview.1",
+            "application/vnd.pypi.simple.v1+json",
+            "application/octet-stream",
+        ],
     )
     async def test_get_remote_projects_pep691_json(self, content_type, 
pypistage):
         pypistage.xom.http.mockresponse(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/test_devpi_server/test_nginx.py 
new/devpi_server-6.21.0/test_devpi_server/test_nginx.py
--- old/devpi_server-6.20.3/test_devpi_server/test_nginx.py     2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/test_devpi_server/test_nginx.py     2026-10-25 
08:08:59.768721600 +0100
@@ -6,6 +6,7 @@
 import json
 import pytest
 import re
+import sys
 
 
 class NoRedirect(HTTPRedirectHandler):
@@ -47,8 +48,7 @@
 
 
 class TestWithOutsideURLHeader:
-    @pytest.fixture(scope="class")
-    def adjust_nginx_conf_content(self):
+    def adjust_nginx_conf_content(cls):
         def adjust_nginx_conf_content(content):
             return re.sub(
                 'proxy_set_header X-outside-url.*$',
@@ -57,6 +57,10 @@
                 flags=re.IGNORECASE | re.MULTILINE)
         return adjust_nginx_conf_content
 
+    if sys.version_info >= (3, 10):
+        adjust_nginx_conf_content = classmethod(adjust_nginx_conf_content)  # 
type: ignore[arg-type, assignment]
+    adjust_nginx_conf_content = pytest.fixture(adjust_nginx_conf_content, 
scope="class")
+
     @pytest.mark.slow
     def test_outside_url_nginx(self, nginx_host_port):
         (host, port) = nginx_host_port
@@ -69,8 +73,7 @@
 
 
 class TestWithOutsideURLSubPathHeader:
-    @pytest.fixture(scope="class")
-    def adjust_nginx_conf_content(self):
+    def adjust_nginx_conf_content(cls):
         def adjust_nginx_conf_content(content):
             return re.sub(
                 'proxy_set_header X-outside-url.*$',
@@ -79,6 +82,10 @@
                 flags=re.IGNORECASE | re.MULTILINE)
         return adjust_nginx_conf_content
 
+    if sys.version_info >= (3, 10):
+        adjust_nginx_conf_content = classmethod(adjust_nginx_conf_content)  # 
type: ignore[arg-type, assignment]
+    adjust_nginx_conf_content = pytest.fixture(adjust_nginx_conf_content, 
scope="class")
+
     @pytest.mark.slow
     def test_outside_url_nginx(self, nginx_host_port):
         (host, port) = nginx_host_port
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/devpi_server-6.20.3/test_devpi_server/test_views.py 
new/devpi_server-6.21.0/test_devpi_server/test_views.py
--- old/devpi_server-6.20.3/test_devpi_server/test_views.py     2026-06-30 
18:36:41.000000000 +0200
+++ new/devpi_server-6.21.0/test_devpi_server/test_views.py     2026-10-25 
10:25:17.803544800 +0100
@@ -59,6 +59,8 @@
         ("foo~bar42", "fatal"),
         (":foobar", "fatal"),
         (":foobar:", "fatal"),
+        (".", "fatal"),
+        ("..", "fatal"),
         ("föö", "ok"),
         ("\N{GRINNING FACE}", "ok"),
     ],

Reply via email to