Skip to content

Commit e7a0746

Browse files
committed
Use git source rules for validating gitdir: paths
1 parent 57c29ef commit e7a0746

4 files changed

Lines changed: 90 additions & 29 deletions

File tree

.gitignore

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
/vendor/
1+
/build/
22
/tools/
3-
/composer.lock
3+
/vendor/
44
/.cs-check.json
5-
/build
5+
/.phpunit.result.cache
6+
/composer.lock

src/DotGit.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,17 @@ private function __construct(string $pathToDotGit)
5757
$this->gitDir = $pathToDotGit;
5858
return;
5959
}
60-
// additional worktree with a .git file referencing the original .git directory
60+
// Additional worktree with a .git file referencing the original .git directory.
6161
// The referenced path is not required to be named '.git'; a worktree of a
6262
// bare repository, or of one created with --separate-git-dir, points at a
6363
// directory with any name. is_dir() below is the actual validation.
64+
// This code aligns with the git source code in 'setup.c'.
6465
if (is_file($pathToDotGit)) {
6566
$dotGitContent = (string) file_get_contents($pathToDotGit);
66-
$match = [];
67-
preg_match('#^gitdir:\s*(?<gitdir>.+)$#m', $dotGitContent, $match);
68-
$dir = rtrim($match['gitdir'] ?? '');
67+
if (!str_starts_with($dotGitContent, 'gitdir: ')) {
68+
throw new RuntimeException('invalid .git file');
69+
}
70+
$dir = rtrim(substr($dotGitContent, 8), "\r\n");
6971
if (is_dir($dir)) {
7072
$this->gitDir = $dir;
7173
$this->isAdditionalWorktree = true;

tests/unit/DotGitTest.php

Lines changed: 0 additions & 22 deletions
This file was deleted.

tests/unit/GitDirParseTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/**
4+
* This file is part of CaptainHook.
5+
*
6+
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace CaptainHook\HookInstaller;
13+
14+
use PHPUnit\Framework\Attributes\DataProvider;
15+
use PHPUnit\Framework\TestCase;
16+
use RuntimeException;
17+
18+
class GitDirParseTest extends TestCase
19+
{
20+
/**
21+
* @link https://github.com/captainhook-git/hook-installer/pull/5#discussion_r3729235534
22+
*/
23+
public static function gitDirFormatsInvalidData(): array
24+
{
25+
return [
26+
'no space after colon' => [
27+
"gitdir:/foo/.bare\r",
28+
],
29+
30+
'invalid whitespace after colon' => [
31+
"gitdir:\n/foo/.bare\n\n",
32+
],
33+
34+
'text before gitdir' => [
35+
"header\ngitdir: /foo/.bare",
36+
],
37+
];
38+
}
39+
40+
#[DataProvider('gitDirFormatsInvalidData')]
41+
public function testParseGitDirInvalid(string $input): void
42+
{
43+
$this->expectException(RuntimeException::class);
44+
$this->expectExceptionMessage('invalid .git file');
45+
46+
$path = $this->writeTempDotGit($input);
47+
48+
DotGit::searchInPath($path);
49+
}
50+
51+
private function writeTempDotGit(string $content): string
52+
{
53+
$dir = $this->createTempDir();
54+
55+
if (file_put_contents("$dir/.git", $content) === false) {
56+
throw new RuntimeException('Failed to create temporary git path');
57+
}
58+
59+
return $dir;
60+
}
61+
62+
private function createTempDir(): string
63+
{
64+
$path = tempnam(sys_get_temp_dir(), 'captainhook-hook-installer-');
65+
66+
if ($path === false) {
67+
throw new RuntimeException('Failed to create temporary path');
68+
}
69+
70+
if (unlink($path) === false) {
71+
throw new RuntimeException('Failed to delete temporary path');
72+
}
73+
74+
if (mkdir($path, 0700) === false) {
75+
throw new RuntimeException('Failed to create temporary directory');
76+
}
77+
78+
return $path;
79+
}
80+
}

0 commit comments

Comments
 (0)