File CVE-2026-32716.patch of Package python-scitokens
From 7a237c0f642efb9e8c36ac564b745895cca83583 Mon Sep 17 00:00:00 2001
From: Derek Weitzel <djw8605@gmail.com>
Date: Fri, 13 Mar 2026 10:49:49 -0500
Subject: [PATCH] Add scope path matching logic and corresponding unit tests
for Enforcer
---
src/scitokens/scitokens.py | 15 +++++++++++---
tests/test_scitokens.py | 40 ++++++++++++++++++++++++++++++++++++++
2 files changed, 52 insertions(+), 3 deletions(-)
Index: scitokens-1.8.1/src/scitokens/scitokens.py
===================================================================
--- scitokens-1.8.1.orig/src/scitokens/scitokens.py
+++ scitokens-1.8.1/src/scitokens/scitokens.py
@@ -679,6 +679,16 @@ class Enforcer(object):
norm_path = '/'
return (authz, norm_path)
+ @staticmethod
+ def _scope_path_matches(requested_path, allowed_path):
+ if allowed_path == '/':
+ return True
+ if requested_path == allowed_path:
+ return True
+ if allowed_path.endswith('/'):
+ return requested_path.startswith(allowed_path)
+ return requested_path.startswith(allowed_path + '/')
+
def _validate_scp(self, value):
if not isinstance(value, list):
value = [value]
@@ -689,7 +699,7 @@ class Enforcer(object):
norm_requested_path = urltools.normalize_path(self._test_path)
for scope in value:
authz, norm_path = self._check_scope(scope)
- if (self._test_authz == authz) and norm_requested_path.startswith(norm_path):
+ if (self._test_authz == authz) and self._scope_path_matches(norm_requested_path, norm_path):
return True
return False
else:
@@ -709,7 +719,7 @@ class Enforcer(object):
# Split on spaces
for scope in value.split(" "):
authz, norm_path = self._check_scope(scope)
- if (self._test_authz == authz) and norm_requested_path.startswith(norm_path):
+ if (self._test_authz == authz) and self._scope_path_matches(norm_requested_path, norm_path):
return True
return False
else:
@@ -718,4 +728,3 @@ class Enforcer(object):
authz, norm_path = self._check_scope(scope)
self._token_scopes.add((authz, norm_path))
return True
-
Index: scitokens-1.8.1/tests/test_scitokens.py
===================================================================
--- scitokens-1.8.1.orig/tests/test_scitokens.py
+++ scitokens-1.8.1/tests/test_scitokens.py
@@ -193,6 +193,26 @@ class TestEnforcer(unittest.TestCase):
with self.assertRaises(scitokens.scitokens.InvalidPathError):
print(enf.test(self._token, "write", "~/foo"))
+ def test_enforce_scp_path_boundaries(self):
+ enf = scitokens.Enforcer(self._test_issuer)
+ enf.add_validator("foo", self.always_accept)
+
+ self._token["scp"] = ["read:/john"]
+ self.assertTrue(enf.test(self._token, "read", "/john"), msg=enf.last_failure)
+ self.assertTrue(enf.test(self._token, "read", "/john/file"), msg=enf.last_failure)
+ self.assertFalse(enf.test(self._token, "read", "/johnathan"), msg=enf.last_failure)
+ self.assertFalse(enf.test(self._token, "read", "/johnny"), msg=enf.last_failure)
+
+ self._token["scp"] = ["read:/john/file"]
+ self.assertFalse(enf.test(self._token, "read", "/john"), msg=enf.last_failure)
+
+ self._token["scp"] = ["read:/"]
+ self.assertTrue(enf.test(self._token, "read", "/arbitrary/path"), msg=enf.last_failure)
+
+ self._token["scp"] = ["read://john"]
+ self.assertTrue(enf.test(self._token, "read", "//john//file"), msg=enf.last_failure)
+ self.assertFalse(enf.test(self._token, "read", "//johnathan"), msg=enf.last_failure)
+
def test_enforce_scope(self):
"""
Test the Enforcer object.
@@ -225,6 +245,26 @@ class TestEnforcer(unittest.TestCase):
with self.assertRaises(scitokens.scitokens.InvalidPathError):
print(enf.test(self._token, "write", "~/foo"))
+ def test_enforce_scope_path_boundaries(self):
+ enf = scitokens.Enforcer(self._test_issuer)
+ enf.add_validator("foo", self.always_accept)
+
+ self._token["scope"] = "read:/john"
+ self.assertTrue(enf.test(self._token, "read", "/john"), msg=enf.last_failure)
+ self.assertTrue(enf.test(self._token, "read", "/john/file"), msg=enf.last_failure)
+ self.assertFalse(enf.test(self._token, "read", "/johnathan"), msg=enf.last_failure)
+ self.assertFalse(enf.test(self._token, "read", "/johnny"), msg=enf.last_failure)
+
+ self._token["scope"] = "read:/john/file"
+ self.assertFalse(enf.test(self._token, "read", "/john"), msg=enf.last_failure)
+
+ self._token["scope"] = "read:/"
+ self.assertTrue(enf.test(self._token, "read", "/arbitrary/path"), msg=enf.last_failure)
+
+ self._token["scope"] = "read://john"
+ self.assertTrue(enf.test(self._token, "read", "//john//file"), msg=enf.last_failure)
+ self.assertFalse(enf.test(self._token, "read", "//johnathan"), msg=enf.last_failure)
+
def test_aud(self):
"""