Skip to content

Commit 29ebc87

Browse files
authored
Fix bugs and hardening issues found by the code audit (#239)
- ReferenceBag::getFirstBranch() mixed up two different arrays (reset($this->branches) then current($this->references)) and could return a non-branch reference, or even throw, when no branch exists. It now correctly returns the first branch or false. - Tag::getBodyMessage() used array_shift()+array_pop() instead of the two array_shift() used by Commit::getBodyMessage(), leaving a stray leading newline (or truncating the last line) for annotated tags using the standard "subject\n\nbody" convention. The body extraction now handles both the blank-line-separated and non-separated forms. - Repository::run() can return null instead of throwing when debug=false and the git command fails, but several call sites fed that null straight into trim()/explode()/Diff::parse() or a non-nullable return type. Depending on the call site this silently produced wrong data (Tag::isAnnotated() reporting true for a lightweight tag, PushReference::isForce() defaulting to a coin flip) or crashed with a TypeError (Blob::getContent()). All call sites in Commit, Tag, Blob, Reference, PushReference, Repository and WorkingCopy now check for null and fail predictably. - Repository::shell() interpolated the repository path into the shell command without escaping it (only the env vars were escaped); it is now passed through escapeshellarg(). - Hooks::set() created hook scripts as 0777 (world-writable); they are now created 0700, readable/writable/executable by the owner only. - Replaced the untyped array<string, mixed> "data bag" backing Commit and Reference\Tag with real typed properties, validated at the one point external data enters (setData()/the parser). This removes the Commit.php and Reference\Tag.php entries from the PHPStan baseline (89 -> 51 ignored errors) without resorting to assert()/@var casts. - Minor: fixed the isSuccessFul() casing typo in Admin.php, and dropped composer.json's leftover minimum-stability: dev (dependency resolution is unaffected, verified with composer update --dry-run). Added regression tests for the two bugs, the debug=false fixes, and the new hook permissions.
1 parent 9fd35df commit 29ebc87

16 files changed

Lines changed: 380 additions & 363 deletions

composer.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,5 @@
4949
"preferred-install": "dist",
5050
"sort-packages": true
5151
},
52-
"minimum-stability": "dev",
5352
"prefer-stable": true
5453
}

phpstan-baseline.neon

Lines changed: 0 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -78,162 +78,6 @@ parameters:
7878
count: 1
7979
path: src/Gitonomy/Git/Blame/Line.php
8080

81-
-
82-
message: '#^Method Gitonomy\\Git\\Blob\:\:getContent\(\) should return string but returns string\|null\.$#'
83-
identifier: return.type
84-
count: 1
85-
path: src/Gitonomy/Git/Blob.php
86-
87-
-
88-
message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#'
89-
identifier: foreach.nonIterable
90-
count: 1
91-
path: src/Gitonomy/Git/Commit.php
92-
93-
-
94-
message: '#^Method Gitonomy\\Git\\Commit\:\:__construct\(\) has parameter \$data with no value type specified in iterable type array\.$#'
95-
identifier: missingType.iterableValue
96-
count: 1
97-
path: src/Gitonomy/Git/Commit.php
98-
99-
-
100-
message: '#^Method Gitonomy\\Git\\Commit\:\:getAuthorDate\(\) should return DateTime but returns mixed\.$#'
101-
identifier: return.type
102-
count: 1
103-
path: src/Gitonomy/Git/Commit.php
104-
105-
-
106-
message: '#^Method Gitonomy\\Git\\Commit\:\:getAuthorEmail\(\) should return string but returns mixed\.$#'
107-
identifier: return.type
108-
count: 1
109-
path: src/Gitonomy/Git/Commit.php
110-
111-
-
112-
message: '#^Method Gitonomy\\Git\\Commit\:\:getAuthorName\(\) should return string but returns mixed\.$#'
113-
identifier: return.type
114-
count: 1
115-
path: src/Gitonomy/Git/Commit.php
116-
117-
-
118-
message: '#^Method Gitonomy\\Git\\Commit\:\:getBodyMessage\(\) should return string but returns mixed\.$#'
119-
identifier: return.type
120-
count: 1
121-
path: src/Gitonomy/Git/Commit.php
122-
123-
-
124-
message: '#^Method Gitonomy\\Git\\Commit\:\:getCommitterDate\(\) should return DateTime but returns mixed\.$#'
125-
identifier: return.type
126-
count: 1
127-
path: src/Gitonomy/Git/Commit.php
128-
129-
-
130-
message: '#^Method Gitonomy\\Git\\Commit\:\:getCommitterEmail\(\) should return string but returns mixed\.$#'
131-
identifier: return.type
132-
count: 1
133-
path: src/Gitonomy/Git/Commit.php
134-
135-
-
136-
message: '#^Method Gitonomy\\Git\\Commit\:\:getCommitterName\(\) should return string but returns mixed\.$#'
137-
identifier: return.type
138-
count: 1
139-
path: src/Gitonomy/Git/Commit.php
140-
141-
-
142-
message: '#^Method Gitonomy\\Git\\Commit\:\:getMessage\(\) should return string but returns mixed\.$#'
143-
identifier: return.type
144-
count: 1
145-
path: src/Gitonomy/Git/Commit.php
146-
147-
-
148-
message: '#^Method Gitonomy\\Git\\Commit\:\:getParentHashes\(\) should return array\<string\> but returns mixed\.$#'
149-
identifier: return.type
150-
count: 1
151-
path: src/Gitonomy/Git/Commit.php
152-
153-
-
154-
message: '#^Method Gitonomy\\Git\\Commit\:\:getShortHash\(\) should return string but returns mixed\.$#'
155-
identifier: return.type
156-
count: 1
157-
path: src/Gitonomy/Git/Commit.php
158-
159-
-
160-
message: '#^Method Gitonomy\\Git\\Commit\:\:getShortMessage\(\) should return string but returns mixed\.$#'
161-
identifier: return.type
162-
count: 1
163-
path: src/Gitonomy/Git/Commit.php
164-
165-
-
166-
message: '#^Method Gitonomy\\Git\\Commit\:\:getSubjectMessage\(\) should return string but returns mixed\.$#'
167-
identifier: return.type
168-
count: 1
169-
path: src/Gitonomy/Git/Commit.php
170-
171-
-
172-
message: '#^Method Gitonomy\\Git\\Commit\:\:getTree\(\) should return Gitonomy\\Git\\Tree but returns mixed\.$#'
173-
identifier: return.type
174-
count: 1
175-
path: src/Gitonomy/Git/Commit.php
176-
177-
-
178-
message: '#^Method Gitonomy\\Git\\Commit\:\:getTreeHash\(\) should return string but returns mixed\.$#'
179-
identifier: return.type
180-
count: 1
181-
path: src/Gitonomy/Git/Commit.php
182-
183-
-
184-
message: '#^Method Gitonomy\\Git\\Commit\:\:setData\(\) has parameter \$data with no value type specified in iterable type array\.$#'
185-
identifier: missingType.iterableValue
186-
count: 1
187-
path: src/Gitonomy/Git/Commit.php
188-
189-
-
190-
message: '#^Parameter \#1 \$hash of method Gitonomy\\Git\\Repository\:\:getCommit\(\) expects string, mixed given\.$#'
191-
identifier: argument.type
192-
count: 1
193-
path: src/Gitonomy/Git/Commit.php
194-
195-
-
196-
message: '#^Parameter \#1 \$hash of method Gitonomy\\Git\\Repository\:\:getTree\(\) expects string, mixed given\.$#'
197-
identifier: argument.type
198-
count: 1
199-
path: src/Gitonomy/Git/Commit.php
200-
201-
-
202-
message: '#^Parameter \#1 \$haystack of static method Gitonomy\\Git\\Util\\StringHelper\:\:strpos\(\) expects string, mixed given\.$#'
203-
identifier: argument.type
204-
count: 1
205-
path: src/Gitonomy/Git/Commit.php
206-
207-
-
208-
message: '#^Parameter \#1 \$rawDiff of static method Gitonomy\\Git\\Diff\\Diff\:\:parse\(\) expects string, string\|null given\.$#'
209-
identifier: argument.type
210-
count: 1
211-
path: src/Gitonomy/Git/Commit.php
212-
213-
-
214-
message: '#^Parameter \#1 \$string of function trim expects string, string\|null given\.$#'
215-
identifier: argument.type
216-
count: 2
217-
path: src/Gitonomy/Git/Commit.php
218-
219-
-
220-
message: '#^Parameter \#1 \$string of static method Gitonomy\\Git\\Util\\StringHelper\:\:strlen\(\) expects string, mixed given\.$#'
221-
identifier: argument.type
222-
count: 1
223-
path: src/Gitonomy/Git/Commit.php
224-
225-
-
226-
message: '#^Parameter \#1 \$string of static method Gitonomy\\Git\\Util\\StringHelper\:\:substr\(\) expects string, mixed given\.$#'
227-
identifier: argument.type
228-
count: 1
229-
path: src/Gitonomy/Git/Commit.php
230-
231-
-
232-
message: '#^Parameter \#2 \$string of function explode expects string, mixed given\.$#'
233-
identifier: argument.type
234-
count: 2
235-
path: src/Gitonomy/Git/Commit.php
236-
23781
-
23882
message: '#^Method Gitonomy\\Git\\Diff\\Diff\:\:fromArray\(\) has parameter \$array with no value type specified in iterable type array\.$#'
23983
identifier: missingType.iterableValue
@@ -336,72 +180,12 @@ parameters:
336180
count: 1
337181
path: src/Gitonomy/Git/Parser/ParserBase.php
338182

339-
-
340-
message: '#^Parameter \#1 \$string of function trim expects string, string\|null given\.$#'
341-
identifier: argument.type
342-
count: 1
343-
path: src/Gitonomy/Git/PushReference.php
344-
345-
-
346-
message: '#^Parameter \#1 \$string of function trim expects string, string\|null given\.$#'
347-
identifier: argument.type
348-
count: 1
349-
path: src/Gitonomy/Git/Reference.php
350-
351183
-
352184
message: '#^Method Gitonomy\\Git\\Reference\\Branch\:\:isLocal\(\) should return bool but returns bool\|null\.$#'
353185
identifier: return.type
354186
count: 1
355187
path: src/Gitonomy/Git/Reference/Branch.php
356188

357-
-
358-
message: '#^Method Gitonomy\\Git\\Reference\\Tag\:\:getBodyMessage\(\) should return string\|false but returns mixed\.$#'
359-
identifier: return.type
360-
count: 1
361-
path: src/Gitonomy/Git/Reference/Tag.php
362-
363-
-
364-
message: '#^Method Gitonomy\\Git\\Reference\\Tag\:\:getGPGSignature\(\) should return string\|false but returns mixed\.$#'
365-
identifier: return.type
366-
count: 1
367-
path: src/Gitonomy/Git/Reference/Tag.php
368-
369-
-
370-
message: '#^Method Gitonomy\\Git\\Reference\\Tag\:\:getMessage\(\) should return string\|false but returns mixed\.$#'
371-
identifier: return.type
372-
count: 1
373-
path: src/Gitonomy/Git/Reference/Tag.php
374-
375-
-
376-
message: '#^Method Gitonomy\\Git\\Reference\\Tag\:\:getSubjectMessage\(\) should return string\|false but returns mixed\.$#'
377-
identifier: return.type
378-
count: 1
379-
path: src/Gitonomy/Git/Reference/Tag.php
380-
381-
-
382-
message: '#^Method Gitonomy\\Git\\Reference\\Tag\:\:getTaggerDate\(\) should return DateTime\|false but returns mixed\.$#'
383-
identifier: return.type
384-
count: 1
385-
path: src/Gitonomy/Git/Reference/Tag.php
386-
387-
-
388-
message: '#^Method Gitonomy\\Git\\Reference\\Tag\:\:getTaggerEmail\(\) should return string\|false but returns mixed\.$#'
389-
identifier: return.type
390-
count: 1
391-
path: src/Gitonomy/Git/Reference/Tag.php
392-
393-
-
394-
message: '#^Method Gitonomy\\Git\\Reference\\Tag\:\:getTaggerName\(\) should return string\|false but returns mixed\.$#'
395-
identifier: return.type
396-
count: 1
397-
path: src/Gitonomy/Git/Reference/Tag.php
398-
399-
-
400-
message: '#^Parameter \#2 \$string of function explode expects string, mixed given\.$#'
401-
identifier: argument.type
402-
count: 2
403-
path: src/Gitonomy/Git/Reference/Tag.php
404-
405189
-
406190
message: '#^Class Gitonomy\\Git\\ReferenceBag implements generic interface IteratorAggregate but does not specify its types\: TKey, TValue$#'
407191
identifier: missingType.generics
@@ -468,12 +252,6 @@ parameters:
468252
count: 1
469253
path: src/Gitonomy/Git/Repository.php
470254

471-
-
472-
message: '#^Parameter \#1 \$rawDiff of static method Gitonomy\\Git\\Diff\\Diff\:\:parse\(\) expects string, string\|null given\.$#'
473-
identifier: argument.type
474-
count: 1
475-
path: src/Gitonomy/Git/Repository.php
476-
477255
-
478256
message: '#^Parameter \#1 \$string of function trim expects string, string\|false given\.$#'
479257
identifier: argument.type
@@ -521,15 +299,3 @@ parameters:
521299
identifier: missingType.iterableValue
522300
count: 1
523301
path: src/Gitonomy/Git/WorkingCopy.php
524-
525-
-
526-
message: '#^Parameter \#1 \$rawDiff of static method Gitonomy\\Git\\Diff\\Diff\:\:parse\(\) expects string, string\|null given\.$#'
527-
identifier: argument.type
528-
count: 2
529-
path: src/Gitonomy/Git/WorkingCopy.php
530-
531-
-
532-
message: '#^Parameter \#2 \$string of function explode expects string, string\|null given\.$#'
533-
identifier: argument.type
534-
count: 1
535-
path: src/Gitonomy/Git/WorkingCopy.php

src/Gitonomy/Git/Admin.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static function init(string $path, bool $bare = true, array $options = []
3737

3838
$process->run();
3939

40-
if (!$process->isSuccessFul()) {
40+
if (!$process->isSuccessful()) {
4141
throw new RuntimeException(\sprintf("Error on repository initialization, command wasn't successful (%s). Error output:\n%s", $process->getCommandLine(), $process->getErrorOutput()));
4242
}
4343

@@ -62,7 +62,7 @@ public static function isValidRepository(string $url, array $options = []): bool
6262

6363
$process->run();
6464

65-
return $process->isSuccessFul();
65+
return $process->isSuccessful();
6666
}
6767

6868
/**
@@ -86,7 +86,7 @@ public static function isValidRepositoryAndBranch(string $url, string $branchNam
8686
$process->run();
8787
$processOutput = $process->getOutput();
8888

89-
return $process->isSuccessFul() && str_contains($processOutput, $branchName);
89+
return $process->isSuccessful() && str_contains($processOutput, $branchName);
9090
}
9191

9292
/**
@@ -149,7 +149,7 @@ public static function cloneRepository(string $path, string $url, array $args =
149149

150150
$process->run();
151151

152-
if (!$process->isSuccessFul()) {
152+
if (!$process->isSuccessful()) {
153153
throw new RuntimeException(\sprintf('Error while initializing repository: %s', $process->getErrorOutput()));
154154
}
155155

src/Gitonomy/Git/Blob.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
namespace Gitonomy\Git;
1414

15+
use Gitonomy\Git\Exception\ReferenceNotFoundException;
16+
1517
/**
1618
* Representation of a Blob commit.
1719
*
@@ -53,7 +55,12 @@ public function getHash(): string
5355
public function getContent(): string
5456
{
5557
if (null === $this->content) {
56-
$this->content = $this->repository->run('cat-file', ['-p', $this->hash]);
58+
$content = $this->repository->run('cat-file', ['-p', $this->hash]);
59+
if (null === $content) {
60+
throw new ReferenceNotFoundException($this->hash);
61+
}
62+
63+
$this->content = $content;
5764
}
5865

5966
return $this->content;

0 commit comments

Comments
 (0)