Git.pm: trust rev-parse to find bare repositories
When initializing a repository object, we run "git rev-parse --git-dir" to let the C version of Git find the correct directory. But curiously, if this fails we don't automatically say "not a git repository". Instead, we do our own pure-perl check to see if we're in a bare repository. This makes little sense, as rev-parse will report both bare and non-bare directories. This logic comes fromd5c7721d58
(Git.pm: Add support for subdirectories inside of working copies, 2006-06-24), but I don't see any reason given why we can't just rely on rev-parse. Worse, because we treat any non-error response from rev-parse as a non-bare repository, we'll erroneously set the object's WorkingCopy, even in a bare repository. But it gets worse. Since8959555cee
(setup_git_directory(): add an owner check for the top-level directory, 2022-03-02), it's actively wrong (and dangerous). The perl code doesn't implement the same ownership checks. And worse, after "finding" the bare repository, it sets GIT_DIR in the environment, which tells any subsequent Git commands that we've confirmed the directory is OK, and to trust us. I.e., it re-opens the vulnerability plugged by8959555cee
when using Git.pm's repository discovery code. We can fix this by just relying on rev-parse to tell us when we're not in a repository, which fixes the vulnerability. Furthermore, we'll ask its --is-bare-repository function to tell us if we're bare or not, and rely on that. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
77a1310e6b
commit
20da61f25f
34
perl/Git.pm
34
perl/Git.pm
@ -177,16 +177,27 @@ sub repository {
|
|||||||
-d $opts{Directory} or throw Error::Simple("Directory not found: $opts{Directory} $!");
|
-d $opts{Directory} or throw Error::Simple("Directory not found: $opts{Directory} $!");
|
||||||
|
|
||||||
my $search = Git->repository(WorkingCopy => $opts{Directory});
|
my $search = Git->repository(WorkingCopy => $opts{Directory});
|
||||||
my $dir;
|
|
||||||
|
# This rev-parse will throw an exception if we're not in a
|
||||||
|
# repository, which is what we want, but it's kind of noisy.
|
||||||
|
# Ideally we'd capture stderr and relay it, but doing so is
|
||||||
|
# awkward without depending on it fitting in a pipe buffer. So
|
||||||
|
# we just reproduce a plausible error message ourselves.
|
||||||
|
my $out;
|
||||||
try {
|
try {
|
||||||
$dir = $search->command_oneline(['rev-parse', '--git-dir'],
|
# Note that "--is-bare-repository" must come first, as
|
||||||
|
# --git-dir output could contain newlines.
|
||||||
|
$out = $search->command([qw(rev-parse --is-bare-repository --git-dir)],
|
||||||
STDERR => 0);
|
STDERR => 0);
|
||||||
} catch Git::Error::Command with {
|
} catch Git::Error::Command with {
|
||||||
$dir = undef;
|
throw Error::Simple("fatal: not a git repository: $opts{Directory}");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
chomp $out;
|
||||||
|
my ($bare, $dir) = split /\n/, $out, 2;
|
||||||
|
|
||||||
require Cwd;
|
require Cwd;
|
||||||
if ($dir) {
|
if ($bare ne 'true') {
|
||||||
require File::Spec;
|
require File::Spec;
|
||||||
File::Spec->file_name_is_absolute($dir) or $dir = $opts{Directory} . '/' . $dir;
|
File::Spec->file_name_is_absolute($dir) or $dir = $opts{Directory} . '/' . $dir;
|
||||||
$opts{Repository} = Cwd::abs_path($dir);
|
$opts{Repository} = Cwd::abs_path($dir);
|
||||||
@ -204,21 +215,6 @@ sub repository {
|
|||||||
$opts{WorkingSubdir} = $prefix;
|
$opts{WorkingSubdir} = $prefix;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
# A bare repository? Let's see...
|
|
||||||
$dir = $opts{Directory};
|
|
||||||
|
|
||||||
unless (-d "$dir/refs" and -d "$dir/objects" and -e "$dir/HEAD") {
|
|
||||||
# Mimic git-rev-parse --git-dir error message:
|
|
||||||
throw Error::Simple("fatal: Not a git repository: $dir");
|
|
||||||
}
|
|
||||||
my $search = Git->repository(Repository => $dir);
|
|
||||||
try {
|
|
||||||
$search->command('symbolic-ref', 'HEAD');
|
|
||||||
} catch Git::Error::Command with {
|
|
||||||
# Mimic git-rev-parse --git-dir error message:
|
|
||||||
throw Error::Simple("fatal: Not a git repository: $dir");
|
|
||||||
};
|
|
||||||
|
|
||||||
$opts{Repository} = Cwd::abs_path($dir);
|
$opts{Repository} = Cwd::abs_path($dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,10 @@ test_expect_success \
|
|||||||
git config --add test.pathmulti bar
|
git config --add test.pathmulti bar
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'set up bare repository' '
|
||||||
|
git init --bare bare.git
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'use t9700/test.pl to test Git.pm' '
|
test_expect_success 'use t9700/test.pl to test Git.pm' '
|
||||||
"$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl 2>stderr &&
|
"$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl 2>stderr &&
|
||||||
test_must_be_empty stderr
|
test_must_be_empty stderr
|
||||||
|
@ -30,6 +30,18 @@ BEGIN { use_ok('Git') }
|
|||||||
# set up
|
# set up
|
||||||
our $abs_repo_dir = cwd();
|
our $abs_repo_dir = cwd();
|
||||||
ok(our $r = Git->repository(Directory => "."), "open repository");
|
ok(our $r = Git->repository(Directory => "."), "open repository");
|
||||||
|
{
|
||||||
|
local $ENV{GIT_TEST_ASSUME_DIFFERENT_OWNER} = 1;
|
||||||
|
my $failed;
|
||||||
|
|
||||||
|
$failed = eval { Git->repository(Directory => $abs_repo_dir) };
|
||||||
|
ok(!$failed, "reject unsafe non-bare repository");
|
||||||
|
like($@, qr/not a git repository/i, "unsafe error message");
|
||||||
|
|
||||||
|
$failed = eval { Git->repository(Directory => "$abs_repo_dir/bare.git") };
|
||||||
|
ok(!$failed, "reject unsafe bare repository");
|
||||||
|
like($@, qr/not a git repository/i, "unsafe error message");
|
||||||
|
}
|
||||||
|
|
||||||
# config
|
# config
|
||||||
is($r->config("test.string"), "value", "config scalar: string");
|
is($r->config("test.string"), "value", "config scalar: string");
|
||||||
|
Loading…
Reference in New Issue
Block a user