Merge branch 'validate-dotgit'

This applies a patch from TortoiseGit to Git for Windows, too.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin
2026-06-23 20:01:28 +02:00
4 changed files with 72 additions and 0 deletions

View File

@@ -3834,6 +3834,30 @@ static int acls_supported(const char *path)
return 0;
}
int is_valid_windows_path_element(wchar_t ch)
{
// cf. https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
// Windows disallows ASCII control characters 0x000x1F
if (ch < 0x1F)
return false;
// Windows reserved path characters
switch (ch) {
case L'<':
case L'>':
case L':':
case L'"':
case L'/':
case L'\\':
case L'|':
case L'?':
case L'*':
return 0;
default:
return 1;
}
}
int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
{
WCHAR wpath[MAX_PATH];
@@ -3862,6 +3886,15 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
if (!wcsicmp(wpath, home))
return 1;
/* Do not leak NTLM hashes, UNC paths etc. are generally problematic */
if (wpath[0] == L'\\' && !is_valid_windows_path_element(wpath[1])) {
if (report)
strbuf_addf(report,
"'%s' may refer to a non-local directory",
path);
return 0;
}
/* Get the owner SID */
err = GetNamedSecurityInfoW(wpath, SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION |

View File

@@ -53,6 +53,7 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path);
*/
int is_path_owned_by_current_sid(const char *path, struct strbuf *report);
#define is_path_owned_by_current_user is_path_owned_by_current_sid
int is_valid_windows_path_element(wchar_t ch);
/**
* Verifies that the given path is a valid one on Windows.

36
setup.c
View File

@@ -943,6 +943,35 @@ void read_gitfile_error_die(int error_code, const char *path, const char *dir)
}
}
#if (defined _WIN32 || defined __WIN32__)
static int ensure_valid_ownership(const char *gitfile,
const char *worktree, const char *gitdir,
struct strbuf *report);
static int is_invalid_dotgit_path(const char *gitfile, const char *potential_gitdir)
{
WCHAR wpath[MAX_PATH];
struct strbuf dir = STRBUF_INIT;
int ret;
if (xutftowcs_path(wpath, potential_gitdir) < 0)
return 1;
/* Do not leak NTLM hashes, UNC paths etc. are generally problematic */
if (wpath[0] != L'\\' || is_valid_windows_path_element(wpath[1]))
return 0;
strbuf_addstr(&dir, gitfile);
strbuf_strip_file_from_path(&dir);
ret = ensure_valid_ownership(gitfile, dir.buf, potential_gitdir, NULL) ? 0 : 1;
strbuf_release(&dir);
return ret;
}
#endif
/*
* Try to read the location of the git directory from the .git file,
* return path to git directory if found. The return value comes from
@@ -1016,6 +1045,13 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
free(buf);
buf = dir;
}
#if (defined _WIN32 || defined __WIN32__)
if (is_dir_sep(dir[0]) && is_invalid_dotgit_path(path, dir)) {
strbuf_add(&realpath, dir, strlen(dir));
path = realpath.buf;
goto cleanup_return;
}
#endif
if (!is_git_directory(dir)) {
error_code = READ_GITFILE_ERR_NOT_A_REPO;
goto cleanup_return;

View File

@@ -53,6 +53,8 @@ test_expect_success clone '
'
test_expect_success 'clone without file://' '
test_must_fail git clone "$UNCPATH" clone-without-file &&
git config set --global --append safe.directory "$UNCPATH/.git" &&
git clone "$UNCPATH" clone-without-file
'