From 1ecfad6f9313a3e1043ec010f83655cce4cb3c1e Mon Sep 17 00:00:00 2001 From: Ian Wienand Date: Fri, 31 Oct 2025 17:25:13 +1100 Subject: [PATCH] git: Exit clearly when in a "dubious" directory I hit a _very_ hard to diagnose issue today after switching part of a CI environment. It turns out that due to non-privileged container magic the CI step was not running as the user that cloned the repo. This manifested as the sdist generated during this step just not having a few extra data files; but the build still passed. So several steps later, suddenly files have just vanished from the sdist, which of course is not something that is checked for and so testing blows up in very strange ways. After I understood what was going on, there is a _tiny_ little message hidden amongst the logs that gives a hint of what's going on. ... writing requirements to src/proj.egg-info/requires.txt writing top-level names to src/proj.egg-info/top_level.txt listing git files failed - pretending there aren't any reading manifest file 'src/proj.egg-info/SOURCES.txt' writing manifest file 'src/proj.egg-info/SOURCES.txt' ... What is actually happening is that if you run git you get "git status fatal: detected dubious ownership in repository at '/..proj'". This is the well-known CVE-2022-24765 issue where trusting a `.git` config dir from another user causes problems. In 6a3bb967167545a2ca074ed56a8e4c2280946325 all the calls in setuptools_scm/git.py were updated to use `--git-dir` directly -- git will not complain if you have told it to explicitly trust the config dir like this. However, there are calls in _file_finders/git.py to find the top-level that are not using this. It silently (modulo an easily missed log) skips adding files when this occurs. I can not see that this would ever be the behaviour you would want. If it had of exploded telling me the git call failed, it would have short-cut all of the problem finding. This adds an explicit match on the static part of this git failure message and raises a SystemExit if it hits. A test case that mocks such a situation is added. Closes: https://github.com/pypa/setuptools-scm/issues/784 Signed-off-by: Ian Wienand --- docs/usage.md | 3 +++ src/setuptools_scm/_file_finders/git.py | 12 +++++++++ testing/test_git.py | 34 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index 53f70445..ddae3ba5 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -622,6 +622,9 @@ would be required when not using `setuptools-scm`. - ✅ **Check**: Is setuptools-scm installed in your build environment? - ✅ **Check**: Are you in a valid SCM repository? +**Problem: Error about dubious ownership** +- CVE-2022-24765 identified security issues with git trusting repositories owned by another user. If the file-finder detects that git is unable to list files because it is operating in a git directory owned by another user, it will raise an error. Since this was not the default behaviour of `setuptools_scm` previously, you can override this by setting `SETUPTOOLS_SCM_IGNORE_DUBIOUS_OWNER`, but be warned git will not be listing files correctly with this flag set. + ### Timestamps for Local Development Versions !!! info "Improved Timestamp Behavior" diff --git a/src/setuptools_scm/_file_finders/git.py b/src/setuptools_scm/_file_finders/git.py index 4379c21a..02589898 100644 --- a/src/setuptools_scm/_file_finders/git.py +++ b/src/setuptools_scm/_file_finders/git.py @@ -22,6 +22,18 @@ def _git_toplevel(path: str) -> str | None: cwd = os.path.abspath(path or ".") res = _run(["git", "rev-parse", "HEAD"], cwd=cwd) if res.returncode: + # This catches you being in a git directory, but the + # permissions being incorrect. With modern contanizered + # CI environments you can easily end up in a cloned repo + # with incorrect permissions and we don't want to silently + # ignore files. + if "--add safe.directory" in res.stderr and not os.environ.get( + "SETUPTOOLS_SCM_IGNORE_DUBIOUS_OWNER" + ): + log.error(res.stderr) + raise SystemExit( + "git introspection failed: {}".format(res.stderr.split("\n")[0]) + ) # BAIL if there is no commit log.error("listing git files failed - pretending there aren't any") return None diff --git a/testing/test_git.py b/testing/test_git.py index 31cac7a3..2377e0f3 100644 --- a/testing/test_git.py +++ b/testing/test_git.py @@ -5,6 +5,7 @@ import shutil import subprocess import sys +import textwrap from datetime import date from datetime import datetime @@ -19,6 +20,7 @@ import pytest import setuptools_scm._file_finders +import setuptools_scm._file_finders.git from setuptools_scm import Configuration from setuptools_scm import NonNormalizedVersion @@ -861,3 +863,35 @@ def test_git_no_commits_uses_fallback_version(wd: WorkDir) -> None: assert str(version_no_fallback.tag) == "0.0" assert version_no_fallback.distance == 0 assert version_no_fallback.dirty is True + + +@pytest.mark.issue("https://github.com/pypa/setuptools-scm/issues/784") +def test_dubious_dir( + wd: WorkDir, caplog: pytest.LogCaptureFixture, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test that we exit clearly if we are in a unsafe directory""" + wd.commit_testfile() + git_wd = git.GitWorkdir(wd.cwd) + + def _run(*args, **kwargs) -> CompletedProcess: # type: ignore[no-untyped-def] + """Fake "git rev-parse HEAD" to fail as if you do not own the git repo""" + stderr = textwrap.dedent(f""" + fatal: detected dubious ownership in repository at '{git_wd}' + To add an exception for this directory, call: + + git config --global --add safe.directory /this/is/a/fake/path + """) + orig_run = run + if args[0] == ["git", "rev-parse", "HEAD"]: + return CompletedProcess( + args=[], stdout="%cI", stderr=stderr, returncode=128 + ) + return orig_run(*args, **kwargs) + + monkeypatch.setattr(setuptools_scm._file_finders.git, "_run", _run) + with pytest.raises(SystemExit): + git_find_files(str(wd.cwd)) + + assert "fatal: detected dubious ownership in repository" in " ".join( + caplog.messages + )