This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git


The following commit(s) were added to refs/heads/master by this push:
     new 3ba29f389 another fix for clone task validation: move validation down 
into init_as_clone and do file path and URL validation separately based on 
which is used
3ba29f389 is described below

commit 3ba29f3890047baa75d027998a2c58481b8be6b2
Author: Dave Brondsema <[email protected]>
AuthorDate: Tue May 19 11:23:13 2026 -0400

    another fix for clone task validation: move validation down into 
init_as_clone and do file path and URL validation separately based on which is 
used
---
 Allura/allura/model/repository.py                  | 27 ++++++++++++++++------
 Allura/allura/tasks/repo_tasks.py                  |  3 ---
 .../forgegit/tests/functional/test_controllers.py  |  4 +++-
 ForgeSVN/forgesvn/tests/model/test_repository.py   | 10 ++++----
 ForgeSVN/forgesvn/tests/test_tasks.py              |  3 +--
 5 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/Allura/allura/model/repository.py 
b/Allura/allura/model/repository.py
index b39ca28f1..d61828755 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -18,6 +18,7 @@
 import json
 import os
 import stat
+import sys
 from operator import itemgetter
 import mimetypes
 import logging
@@ -53,13 +54,14 @@
 
 from allura.lib import helpers as h
 from allura.lib import utils
+from allura.lib import validators as v
 from allura.lib.security import has_access
 
 from .artifact import Artifact, VersionedArtifact
 from .auth import User
 from .timeline import ActivityObject
 from .monq_model import MonQTask
-from .project import AppConfig
+from .project import AppConfig, Project
 from .session import main_doc_session
 from .session import repository_orm_session
 
@@ -388,11 +390,11 @@ def activity_name(self):
         return 'repo %s' % self.name
 
     @classmethod
-    def default_fs_path(cls, project, tool):
+    def default_fs_path(cls, project: Project | None, tool: str):
         repos_root = tg.config.get('scm.repos.root', '/')
         # if a user-project, the repository path on disk needs to use the 
actual shortname
         # the nice url might have invalid chars
-        return os.path.join(repos_root, tool, 
project.url(use_userproject_shortname=True)[1:])
+        return os.path.join(repos_root, tool, 
project.url(use_userproject_shortname=True)[1:] if project else '')
 
     @classmethod
     def default_url_path(cls, project, tool):
@@ -538,11 +540,22 @@ def set_default_branch(self, name):
     def paged_diffs(self, commit_id, start=0, end=None, 
onlyChangedFiles=False):
         return self._impl.paged_diffs(commit_id, start, end, onlyChangedFiles)
 
-    def init_as_clone(self, source_path, source_name, source_url):
+    def init_as_clone(self, source_path, source_name, source_url, 
bypass_path_check_for_tests=False):
         self.upstream_repo.name = source_name
         self.upstream_repo.url = source_url
         session(self).flush(self)
-        source = source_path if source_path else source_url
+        if source_path:
+            repos_root = self.default_fs_path(project=None, tool=self.tool)
+            if not source_path.startswith(repos_root) and not 
bypass_path_check_for_tests:
+                err = 'Invalid source path'
+                if asbool(tg.config['debug']) or 'pytest' in sys.modules:
+                        err += f': {source_path} must start with {repos_root}'
+                raise ValueError(err)
+            source = source_path
+        else:
+            # could be git:// svn+ssh:// many things for scheme, so 
enforce_schemes=None
+            v.NonPrivateUrl(enforce_schemes=None).to_python(source_url)
+            source = source_url
         self._impl.clone_from(source)
         log.info('... %r cloned', self)
         g.post_event('repo_cloned', source_url, source_path)
@@ -1111,8 +1124,8 @@ class __mongometa__:
     repo = None
 
     def __init__(self, **kw):
-        for k, v in kw.items():
-            setattr(self, k, v)
+        for k, val in kw.items():
+            setattr(self, k, val)
 
     @property
     def activity_name(self):
diff --git a/Allura/allura/tasks/repo_tasks.py 
b/Allura/allura/tasks/repo_tasks.py
index 6b61bfde7..7838c5c53 100644
--- a/Allura/allura/tasks/repo_tasks.py
+++ b/Allura/allura/tasks/repo_tasks.py
@@ -39,9 +39,6 @@ def init(**kwargs):
 def clone(cloned_from_path, cloned_from_name, cloned_from_url):
     from allura import model as M
     try:
-        # could be git:// svn+ssh:// many things for scheme
-        v.NonPrivateUrl(enforce_schemes=None).to_python(cloned_from_url)
-
         c.app.repo.init_as_clone(
             cloned_from_path,
             cloned_from_name,
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py 
b/ForgeGit/forgegit/tests/functional/test_controllers.py
index 750d7f2d3..4b83848aa 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -635,7 +635,9 @@ def setup_method(self, method):
             c.app.repo.init_as_clone(
                 cloned_from.full_fs_path,
                 cloned_from.app.config.script_name(),
-                cloned_from.full_fs_path)
+                cloned_from.full_fs_path,
+                bypass_path_check_for_tests=True,
+            )
             # Add commit to a forked repo, thus merge requests will not be 
empty
             # clone repo to tmp location first (can't add commit to bare repos
             # directly)
diff --git a/ForgeSVN/forgesvn/tests/model/test_repository.py 
b/ForgeSVN/forgesvn/tests/model/test_repository.py
index 7d218a755..cf3fdfddc 100644
--- a/ForgeSVN/forgesvn/tests/model/test_repository.py
+++ b/ForgeSVN/forgesvn/tests/model/test_repository.py
@@ -740,11 +740,11 @@ def test_url_for_commit(self):
 
     @mock.patch('allura.model.repository.g.post_event')
     def test_init_as_clone(self, post_event):
-        self.repo.init_as_clone('srcpath', 'srcname', 'srcurl')
+        self.repo.init_as_clone(tg.config['scm.repos.root'] + '/svn/srcpath', 
'srcname', 'srcurl')
         assert self.repo.upstream_repo.name == 'srcname'
         assert self.repo.upstream_repo.url == 'srcurl'
-        self.repo._impl.clone_from.assert_called_with('srcpath')
-        post_event.assert_called_once_with('repo_cloned', 'srcurl', 'srcpath')
+        self.repo._impl.clone_from.assert_called_with('/tmp/svn/srcpath')
+        post_event.assert_called_once_with('repo_cloned', 'srcurl', 
'/tmp/svn/srcpath')
 
     def test_latest(self):
         ci = mock.Mock()
@@ -830,7 +830,7 @@ def test_refresh_private(self):
         self.repo.refresh()
 
     def test_push_upstream_context(self):
-        self.repo.init_as_clone('srcpath', '/p/test/svn/', '/p/test/svn/')
+        self.repo.init_as_clone(tg.config['scm.repos.root'] + '/svn/srcpath', 
'/p/test/svn/', '/p/test/svn/')
         old_app_instance = M.Project.app_instance
         try:
             M.Project.app_instance = mock.Mock(return_value=ming.base.Object(
@@ -841,7 +841,7 @@ def test_push_upstream_context(self):
             M.Project.app_instance = old_app_instance
 
     def test_pending_upstream_merges(self):
-        self.repo.init_as_clone('srcpath', '/p/test/svn/', '/p/test/svn/')
+        self.repo.init_as_clone(tg.config['scm.repos.root'] + '/svn/srcpath', 
'/p/test/svn/', '/p/test/svn/')
         old_app_instance = M.Project.app_instance
         try:
             M.Project.app_instance = mock.Mock(return_value=ming.base.Object(
diff --git a/ForgeSVN/forgesvn/tests/test_tasks.py 
b/ForgeSVN/forgesvn/tests/test_tasks.py
index 93e4827c2..cd5fb0bf2 100644
--- a/ForgeSVN/forgesvn/tests/test_tasks.py
+++ b/ForgeSVN/forgesvn/tests/test_tasks.py
@@ -66,8 +66,7 @@ def test_clone(self):
             assert ns + 1 == M.Notification.query.find().count()
 
     def test_clone_internal(self):
-        ns = M.Notification.query.find().count()
-        with mock.patch.object(c.app.repo, 'init_as_clone', autospec=True) as 
f:
+        with mock.patch.object(c.app.repo._impl, 'clone_from', autospec=True) 
as f:
             repo_tasks.clone('foo', 'bar', 'http://localhost/baz')
             M.main_orm_session.flush()
             f.assert_not_called()

Reply via email to