• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ericfortmeyer / activity-log / 20527023976

26 Dec 2025 06:06PM UTC coverage: 42.491% (-0.8%) from 43.337%
20527023976

push

github

web-flow
fix: harden release webhook endpoint (#66)

Signed-off-by: Eric Fortmeyer <e.fortmeyer01@gmail.com>

0 of 49 new or added lines in 2 files covered. (0.0%)

1 existing line in 1 file now uncovered.

348 of 819 relevant lines covered (42.49%)

1.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/AppReleaseEvent.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace EricFortmeyer\ActivityLog;
6

7
use Phpolar\Model\AbstractModel;
8
use Phpolar\Validators\MaxLength;
9
use Phpolar\Validators\Pattern;
10
use Psr\Http\Message\RequestInterface;
11

12
final class AppReleaseEvent extends AbstractModel
13
{
14
    private const EVENT_TYPE_HEADER_KEY = "X-GitHub-Event";
15
    private const HOOK_ID_HEADER_KEY = "X-GitHub-Hook-ID";
16
    private const RELEASE_EVENT_TYPE = "release";
17
    private const ACCEPTABLE_RELEASE_ACTION = ["created"];
18
    public readonly AppRelease $release;
19

20
    #[MaxLength(100)]
21
    #[Pattern("/^[[:digit:]]+$/")]
22
    public int|string $hookId;
23

24
    public function __construct(null|array|object $data = [])
25
    {
26
        $this->release = new AppRelease(
×
27
            is_object($data) === true && property_exists($data, "release") === true
×
28
                ? $data->release
×
29
                : []
×
30
        );
×
31
    }
32

33
    public function isValid(): bool
34
    {
NEW
35
        return parent::isValid() && $this->release->isValid();
×
36
    }
37

38
    public static function fromRequest(RequestInterface $request): self
39
    {
NEW
40
        $requestBody = $request->getBody()->getContents();
×
NEW
41
        $data = json_decode($requestBody);
×
NEW
42
        return new self(is_object($data) ? $data : [])->withHookId(
×
NEW
43
            $request->getHeader(self::HOOK_ID_HEADER_KEY)[0] ?? "invalid!!!"
×
NEW
44
        );
×
45
    }
46

47
    public static function isReleaseEventRequest(RequestInterface $request): bool
48
    {
NEW
49
        return ($request->getHeader(self::EVENT_TYPE_HEADER_KEY)[0] ?? "invalid!!!") === self::RELEASE_EVENT_TYPE;
×
50
    }
51

52
    public static function isCreatedRelease(RequestInterface $request): bool
53
    {
NEW
54
        $json = json_decode($request->getBody()->getContents());
×
NEW
55
        return is_object($json) === true
×
NEW
56
            && property_exists($json, "action") === true
×
NEW
57
            && in_array($json->action, self::ACCEPTABLE_RELEASE_ACTION);
×
58
    }
59

60
    private function withHookId(int|string $hookId): self
61
    {
NEW
62
        $newThis = clone $this;
×
NEW
63
        $newThis->hookId = $hookId;
×
NEW
64
        return $newThis;
×
65
    }
66
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc