From c6be29306493b9362716971829be6d49dcb1d6e0 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:24:53 +0100 Subject: [PATCH 01/23] refactor: AppServiceProvider - Remove unused authentik stuff - Move things to separate functions - Configure commands for production - Configure modals for better error handling - Improve password security by not allowing compromised passwords. - Rename some things to make it clearer. - Sort imports --- app/Providers/AppServiceProvider.php | 52 ++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2ce94201c..3b15f1ee1 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,37 +3,67 @@ namespace App\Providers; use App\Models\PersonalAccessToken; -use Illuminate\Support\Facades\Event; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Http; use Illuminate\Support\ServiceProvider; use Illuminate\Validation\Rules\Password; use Laravel\Sanctum\Sanctum; +use Laravel\Telescope\TelescopeServiceProvider; class AppServiceProvider extends ServiceProvider { public function register(): void { - if ($this->app->environment('local')) { - $this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class); + if (App::isLocal()) { + $this->app->register(TelescopeServiceProvider::class); } } public function boot(): void { - Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) { - $event->extendSocialite('authentik', \SocialiteProviders\Authentik\Provider::class); - }); - Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); + $this->configureCommands(); + $this->configureModels(); + $this->configurePasswords(); + $this->configureSanctumModel(); + $this->configureGitHubHttp(); + } + private function configureCommands(): void + { + if (App::isProduction()) { + DB::prohibitDestructiveCommands(); + } + } + + private function configureModels(): void + { + Model::shouldBeStrict(); + } + + private function configurePasswords(): void + { Password::defaults(function () { - $rule = Password::min(8); + $rule = Password::min(8)->letters(); - return $this->app->isProduction() - ? $rule->mixedCase()->letters()->numbers()->symbols() + return App::isProduction() + ? $rule->mixedCase() + ->numbers() + ->symbols() + ->uncompromised() : $rule; }); + } - Http::macro('github', function (string $api_url, ?string $github_access_token = null) { + private function configureSanctumModel(): void + { + Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); + } + + private function configureGitHubHttp(): void + { + Http::macro('GitHub', function (string $api_url, ?string $github_access_token = null) { if ($github_access_token) { return Http::withHeaders([ 'X-GitHub-Api-Version' => '2022-11-28', From 4d34d689b0420e2b4cde02d90886d00cffdfdef3 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:27:18 +0100 Subject: [PATCH 02/23] refactor: github.php - Rename functions - Consolidate Code - Fix: timing issues with JWT tokens - Clearer error handling --- bootstrap/helpers/github.php | 107 ++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 46 deletions(-) diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index 529ac82b1..cd36845a1 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -12,77 +12,91 @@ use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\Token\Builder; -function generate_github_installation_token(GithubApp $source) +function generateGithubToken(GithubApp $source, string $type) { $signingKey = InMemory::plainText($source->privateKey->private_key); $algorithm = new Sha256; $tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default())); - $now = CarbonImmutable::now(); - $now = $now->setTime($now->format('H'), $now->format('i')); - $issuedToken = $tokenBuilder - ->issuedBy($source->app_id) - ->issuedAt($now) - ->expiresAt($now->modify('+10 minutes')) - ->getToken($algorithm, $signingKey) - ->toString(); - $token = Http::withHeaders([ - 'Authorization' => "Bearer $issuedToken", - 'Accept' => 'application/vnd.github.machine-man-preview+json', - ])->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens"); - if ($token->failed()) { - throw new RuntimeException('Failed to get access token for '.$source->name.' with error: '.data_get($token->json(), 'message', 'no error message found')); - } + $now = CarbonImmutable::now()->setTimezone('UTC'); - return $token->json()['token']; -} - -function generate_github_jwt_token(GithubApp $source) -{ - $signingKey = InMemory::plainText($source->privateKey->private_key); - $algorithm = new Sha256; - $tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default())); - $now = CarbonImmutable::now(); - $now = $now->setTime($now->format('H'), $now->format('i')); - - return $tokenBuilder + $jwt = $tokenBuilder ->issuedBy($source->app_id) ->issuedAt($now->modify('-1 minute')) - ->expiresAt($now->modify('+10 minutes')) + ->expiresAt($now->modify('+8 minutes')) ->getToken($algorithm, $signingKey) ->toString(); + + return match ($type) { + 'jwt' => $jwt, + 'installation' => (function () use ($source, $jwt) { + $response = Http::withHeaders([ + 'Authorization' => "Bearer $jwt", + 'Accept' => 'application/vnd.github.machine-man-preview+json', + ])->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens"); + + if (! $response->successful()) { + throw new RuntimeException("Failed to get installation token for {$source->name} with error: ".data_get($response->json(), 'message', 'no error message found')); + } + + return $response->json()['token']; + })(), + default => throw new \InvalidArgumentException("Unsupported token type: {$type}") + }; +} + +function generateGithubInstallationToken(GithubApp $source) +{ + return generateGithubToken($source, 'installation'); +} + +function generateGithubJwt(GithubApp $source) +{ + return generateGithubToken($source, 'jwt'); } function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $method = 'get', ?array $data = null, bool $throwError = true) { if (is_null($source)) { - throw new \Exception('Not implemented yet.'); + throw new \Exception('Source is required for API calls'); } - if ($source->getMorphClass() === \App\Models\GithubApp::class) { - if ($source->is_public) { - $response = Http::github($source->api_url)->$method($endpoint); + + if ($source->getMorphClass() !== GithubApp::class) { + throw new \InvalidArgumentException("Unsupported source type: {$source->getMorphClass()}"); + } + + if ($source->is_public) { + $response = Http::GitHub($source->api_url)->$method($endpoint); + } else { + $token = generateGithubInstallationToken($source); + if ($data && in_array(strtolower($method), ['post', 'patch', 'put'])) { + $response = Http::GitHub($source->api_url, $token)->$method($endpoint, $data); } else { - $github_access_token = generate_github_installation_token($source); - if ($data && ($method === 'post' || $method === 'patch' || $method === 'put')) { - $response = Http::github($source->api_url, $github_access_token)->$method($endpoint, $data); - } else { - $response = Http::github($source->api_url, $github_access_token)->$method($endpoint); - } + $response = Http::GitHub($source->api_url, $token)->$method($endpoint); } } - $json = $response->json(); - if ($response->failed() && $throwError) { - ray($json); - throw new \Exception("Failed to get data from {$source->name} with error:

".$json['message'].'

Rate Limit resets at: '.Carbon::parse((int) $response->header('X-RateLimit-Reset'))->format('Y-m-d H:i:s').'UTC'); + + if (! $response->successful() && $throwError) { + $resetTime = Carbon::parse((int) $response->header('X-RateLimit-Reset'))->format('Y-m-d H:i:s'); + $errorMessage = data_get($response->json(), 'message', 'no error message found'); + $remainingCalls = $response->header('X-RateLimit-Remaining', '0'); + + throw new \Exception( + "GitHub API call failed:\n". + "Error: {$errorMessage}\n". + "Rate Limit Status:\n". + "- Remaining Calls: {$remainingCalls}\n". + "- Reset Time: {$resetTime} UTC" + ); } return [ 'rate_limit_remaining' => $response->header('X-RateLimit-Remaining'), 'rate_limit_reset' => $response->header('X-RateLimit-Reset'), - 'data' => collect($json), + 'data' => collect($response->json()), ]; } -function get_installation_path(GithubApp $source) +function getInstallationPath(GithubApp $source) { $github = GithubApp::where('uuid', $source->uuid)->first(); $name = str(Str::kebab($github->name)); @@ -90,7 +104,8 @@ function get_installation_path(GithubApp $source) return "$github->html_url/$installation_path/$name/installations/new"; } -function get_permissions_path(GithubApp $source) + +function getPermissionsPath(GithubApp $source) { $github = GithubApp::where('uuid', $source->uuid)->first(); $name = str(Str::kebab($github->name)); From 2c4bdb76bdbc43b00eaf4906de3e604ad95fb8c5 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:27:47 +0100 Subject: [PATCH 03/23] chore: use new functions --- app/Jobs/GithubAppPermissionJob.php | 2 +- app/Livewire/Project/New/GithubPrivateRepository.php | 2 +- app/Livewire/Source/Github/Change.php | 2 +- app/Models/Application.php | 4 ++-- .../views/livewire/project/application/source.blade.php | 2 +- .../project/new/github-private-repository.blade.php | 2 +- resources/views/livewire/source/github/change.blade.php | 6 +++--- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/Jobs/GithubAppPermissionJob.php b/app/Jobs/GithubAppPermissionJob.php index d483fe4c2..eb2cb673b 100644 --- a/app/Jobs/GithubAppPermissionJob.php +++ b/app/Jobs/GithubAppPermissionJob.php @@ -27,7 +27,7 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue public function handle() { try { - $github_access_token = generate_github_jwt_token($this->github_app); + $github_access_token = generateGithubJwt($this->github_app); $response = Http::withHeaders([ 'Authorization' => "Bearer $github_access_token", 'Accept' => 'application/vnd.github+json', diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php index 370d00555..4a81d841f 100644 --- a/app/Livewire/Project/New/GithubPrivateRepository.php +++ b/app/Livewire/Project/New/GithubPrivateRepository.php @@ -105,7 +105,7 @@ class GithubPrivateRepository extends Component $this->page = 1; $this->selected_github_app_id = $github_app_id; $this->github_app = GithubApp::where('id', $github_app_id)->first(); - $this->token = generate_github_installation_token($this->github_app); + $this->token = generateGithubInstallationToken($this->github_app); $this->loadRepositoryByPage(); if ($this->repositories->count() < $this->total_repositories_count) { while ($this->repositories->count() < $this->total_repositories_count) { diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 8f4f02f70..fc597748e 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -76,7 +76,7 @@ class Change extends Component // Need administration:read:write permission // https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-a-repository - // $github_access_token = generate_github_installation_token($this->github_app); + // $github_access_token = generateGithubInstallationToken($this->github_app); // $repositories = Http::withToken($github_access_token)->get("{$this->github_app->api_url}/installation/repositories?per_page=100"); // $runners_by_repository = collect([]); // $repositories = $repositories->json()['repositories']; diff --git a/app/Models/Application.php b/app/Models/Application.php index 13f15468d..fa942c078 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -999,7 +999,7 @@ class Application extends BaseModel $fullRepoUrl = "{$this->source->html_url}/{$customRepository}"; $base_command = "{$base_command} {$this->source->html_url}/{$customRepository}"; } else { - $github_access_token = generate_github_installation_token($this->source); + $github_access_token = generateGithubInstallationToken($this->source); if ($exec_in_docker) { $base_command = "{$base_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git"; @@ -1111,7 +1111,7 @@ class Application extends BaseModel $commands->push($git_clone_command); } } else { - $github_access_token = generate_github_installation_token($this->source); + $github_access_token = generateGithubInstallationToken($this->source); if ($exec_in_docker) { $git_clone_command = "{$git_clone_command} $source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git {$baseDir}"; $fullRepoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git"; diff --git a/resources/views/livewire/project/application/source.blade.php b/resources/views/livewire/project/application/source.blade.php index 4aab498e9..b542d5428 100644 --- a/resources/views/livewire/project/application/source.blade.php +++ b/resources/views/livewire/project/application/source.blade.php @@ -10,7 +10,7 @@ @if (data_get($application, 'source.is_public') === false) - + Open Git App diff --git a/resources/views/livewire/project/new/github-private-repository.blade.php b/resources/views/livewire/project/new/github-private-repository.blade.php index b390189fc..94779e714 100644 --- a/resources/views/livewire/project/new/github-private-repository.blade.php +++ b/resources/views/livewire/project/new/github-private-repository.blade.php @@ -5,7 +5,7 @@ @if ($repositories->count() > 0) - + Change Repositories on GitHub diff --git a/resources/views/livewire/source/github/change.blade.php b/resources/views/livewire/source/github/change.blade.php index 3c11646c2..733f6e433 100644 --- a/resources/views/livewire/source/github/change.blade.php +++ b/resources/views/livewire/source/github/change.blade.php @@ -6,7 +6,7 @@
@if (data_get($github_app, 'installation_id')) Save - + Update Repositories @@ -52,7 +52,7 @@ You must complete this step before you can use this source!
- + Install Repositories on GitHub @else @@ -106,7 +106,7 @@

Permissions

Refetch - + Update From 1a36e7b057c67cf471a08e8629b2dc13fb0ae4b4 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:41:05 +0100 Subject: [PATCH 04/23] fix: add back letters to prod password requirement --- app/Providers/AppServiceProvider.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3b15f1ee1..6ed5e654e 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -49,6 +49,7 @@ class AppServiceProvider extends ServiceProvider return App::isProduction() ? $rule->mixedCase() + ->letters() ->numbers() ->symbols() ->uncompromised() From 05113d8e06a708b4cb21c1f1300f0cd1aca7a71f Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:28:49 +0100 Subject: [PATCH 05/23] fix: check System and GitHub time and throw and error if it is over 50s out of sync --- bootstrap/helpers/github.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index cd36845a1..3ed588bdb 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -14,6 +14,21 @@ use Lcobucci\JWT\Token\Builder; function generateGithubToken(GithubApp $source, string $type) { + $response = Http::get("{$source->api_url}/zen"); + $serverTime = now(); + $githubTime = Carbon::parse($response->header('date')); + $timeDiff = abs($serverTime->diffInSeconds($githubTime)); + + if ($timeDiff > 0) { + throw new \Exception( + "System time is out of sync with GitHub API time:\n". + "- System time: {$serverTime->format('Y-m-d H:i:s')} UTC\n". + "- GitHub time: {$githubTime->format('Y-m-d H:i:s')} UTC\n". + "- Difference: {$timeDiff} seconds\n". + 'Please synchronize your system clock.' + ); + } + $signingKey = InMemory::plainText($source->privateKey->private_key); $algorithm = new Sha256; $tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default())); From 503d2b486605e8d8495086b735eb5588c9c2cafa Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:43:27 +0100 Subject: [PATCH 06/23] fix: error message and server time getting --- bootstrap/helpers/github.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index 3ed588bdb..e646aa738 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -15,16 +15,16 @@ use Lcobucci\JWT\Token\Builder; function generateGithubToken(GithubApp $source, string $type) { $response = Http::get("{$source->api_url}/zen"); - $serverTime = now(); + $serverTime = CarbonImmutable::now()->setTimezone('UTC'); $githubTime = Carbon::parse($response->header('date')); $timeDiff = abs($serverTime->diffInSeconds($githubTime)); if ($timeDiff > 0) { throw new \Exception( - "System time is out of sync with GitHub API time:\n". - "- System time: {$serverTime->format('Y-m-d H:i:s')} UTC\n". - "- GitHub time: {$githubTime->format('Y-m-d H:i:s')} UTC\n". - "- Difference: {$timeDiff} seconds\n". + 'System time is out of sync with GitHub API time:'.PHP_EOL. + '- System time: '.$serverTime->format('Y-m-d H:i:s').' UTC'.PHP_EOL. + '- GitHub time: '.$githubTime->format('Y-m-d H:i:s').' UTC'.PHP_EOL. + '- Difference: '.$timeDiff.' seconds'.PHP_EOL. 'Please synchronize your system clock.' ); } From a29547b3bae008f06d0101dc4c8cf83957e6122a Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:49:55 +0100 Subject: [PATCH 07/23] fix: error rendering --- bootstrap/helpers/github.php | 18 +++++++++--------- resources/views/errors/500.blade.php | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index e646aa738..be9ca9582 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -21,10 +21,10 @@ function generateGithubToken(GithubApp $source, string $type) if ($timeDiff > 0) { throw new \Exception( - 'System time is out of sync with GitHub API time:'.PHP_EOL. - '- System time: '.$serverTime->format('Y-m-d H:i:s').' UTC'.PHP_EOL. - '- GitHub time: '.$githubTime->format('Y-m-d H:i:s').' UTC'.PHP_EOL. - '- Difference: '.$timeDiff.' seconds'.PHP_EOL. + 'System time is out of sync with GitHub API time:
'. + '- System time: '.$serverTime->format('Y-m-d H:i:s').' UTC
'. + '- GitHub time: '.$githubTime->format('Y-m-d H:i:s').' UTC
'. + '- Difference: '.$timeDiff.' seconds
'. 'Please synchronize your system clock.' ); } @@ -96,11 +96,11 @@ function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $m $remainingCalls = $response->header('X-RateLimit-Remaining', '0'); throw new \Exception( - "GitHub API call failed:\n". - "Error: {$errorMessage}\n". - "Rate Limit Status:\n". - "- Remaining Calls: {$remainingCalls}\n". - "- Reset Time: {$resetTime} UTC" + 'GitHub API call failed:
'. + "Error: {$errorMessage}
". + 'Rate Limit Status:
'. + "- Remaining Calls: {$remainingCalls}
". + "- Reset Time: {$resetTime} UTC" ); } diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index 9e556cbdf..c8d6ea0ad 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -3,11 +3,11 @@

500

Wait, this is not cool...

-

There has been an error, we are working on it. -

+

There has been an error, we are working on it.

@if ($exception->getMessage() !== '') - Error: {{ $exception->getMessage() }} - +
+ Error: {!! nl2br(e($exception->getMessage())) !!} +
@endif
From 3adc9cf335f04256a2a808ced71f7ce1bdaf5a30 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:55:37 +0100 Subject: [PATCH 08/23] fix: render html correctly now --- resources/views/errors/500.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index c8d6ea0ad..0dbb3c59b 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -5,8 +5,8 @@

Wait, this is not cool...

There has been an error, we are working on it.

@if ($exception->getMessage() !== '') -
- Error: {!! nl2br(e($exception->getMessage())) !!} +
+ Error: {!! $exception->getMessage() !!}
@endif
From 3c623d6553b8f3656d1390ccc3c34b8d9a24ef45 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:59:17 +0100 Subject: [PATCH 09/23] chore: improve error styling --- resources/views/errors/500.blade.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index 0dbb3c59b..ee254a990 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -1,21 +1,21 @@ @extends('layouts.base') -
-
-

500

-

Wait, this is not cool...

-

There has been an error, we are working on it.

+ +
+
+

500

+

Wait, this is not cool...

@if ($exception->getMessage() !== '') -
+
Error: {!! $exception->getMessage() !!}
@endif -
From 0f77e282cbbcedbaf264c959242d3850776d386c Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:05:58 +0100 Subject: [PATCH 10/23] chore: css --- resources/views/errors/500.blade.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index ee254a990..6e6b78e0d 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -1,22 +1,22 @@ @extends('layouts.base') - -
+
-

500

-

Wait, this is not cool...

+

500

+

Wait, this is not cool...

+

There has been an error, we are working on it.

@if ($exception->getMessage() !== '') -
+
Error: {!! $exception->getMessage() !!}
@endif
-
+
\ No newline at end of file From cb75ca8bea7709bccef5a7c1bb2bd41635faa5c7 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:11:04 +0100 Subject: [PATCH 11/23] chore: more css as it still looks like shit --- resources/views/errors/500.blade.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index 6e6b78e0d..ec66dcc8b 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -1,15 +1,15 @@ @extends('layouts.base') -
-
-

500

+
+
+

500

Wait, this is not cool...

There has been an error, we are working on it.

@if ($exception->getMessage() !== '') -
+
Error: {!! $exception->getMessage() !!}
@endif -
+
Go back home From 099189885350a4bf57c5f9fde4179fba994b5618 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:16:30 +0100 Subject: [PATCH 12/23] chore: final css touches --- resources/views/errors/500.blade.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index ec66dcc8b..67f0d9fef 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -1,20 +1,20 @@ @extends('layouts.base') -
-
-

500

-

Wait, this is not cool...

-

There has been an error, we are working on it.

+
+
+

500

+

Wait, this is not cool...

+

There has been an error, we are working on it.

@if ($exception->getMessage() !== '') -
+
Error: {!! $exception->getMessage() !!}
@endif -
+ From d0c84bc6fa785a085a1af6524dbe764cfb8a71d1 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:22:12 +0100 Subject: [PATCH 13/23] fix: indent --- resources/views/errors/500.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index 67f0d9fef..d59b5bc28 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -3,10 +3,10 @@

500

Wait, this is not cool...

-

There has been an error, we are working on it.

+

There has been an error with the following error message:

@if ($exception->getMessage() !== '')
- Error: {!! $exception->getMessage() !!} + {!! $exception->getMessage() !!}
@endif
From fdb5f069fc3d69bdf9902823d66bd19071a5250e Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:32:05 +0100 Subject: [PATCH 14/23] chore: ajust time to 50s (tests done) --- bootstrap/helpers/github.php | 2 +- resources/views/errors/500.blade.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index be9ca9582..b687c0d68 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -19,7 +19,7 @@ function generateGithubToken(GithubApp $source, string $type) $githubTime = Carbon::parse($response->header('date')); $timeDiff = abs($serverTime->diffInSeconds($githubTime)); - if ($timeDiff > 0) { + if ($timeDiff > 50) { throw new \Exception( 'System time is out of sync with GitHub API time:
'. '- System time: '.$serverTime->format('Y-m-d H:i:s').' UTC
'. diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index d59b5bc28..553264f00 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -5,7 +5,7 @@

Wait, this is not cool...

There has been an error with the following error message:

@if ($exception->getMessage() !== '') -
+
{!! $exception->getMessage() !!}
@endif From 49f468c4b797ea825e4fd64efe5a7e81c44f06aa Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 19:37:51 +0100 Subject: [PATCH 15/23] fix: potential fix for permissions update --- app/Jobs/GithubAppPermissionJob.php | 2 +- resources/views/errors/500.blade.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Jobs/GithubAppPermissionJob.php b/app/Jobs/GithubAppPermissionJob.php index eb2cb673b..1ded6f455 100644 --- a/app/Jobs/GithubAppPermissionJob.php +++ b/app/Jobs/GithubAppPermissionJob.php @@ -30,7 +30,7 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue $github_access_token = generateGithubJwt($this->github_app); $response = Http::withHeaders([ 'Authorization' => "Bearer $github_access_token", - 'Accept' => 'application/vnd.github+json', + 'Accept' => 'application/vnd.github.machine-man-preview+json', ])->get("{$this->github_app->api_url}/app"); $response = $response->json(); $permissions = data_get($response, 'permissions'); diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index 553264f00..10e9649d7 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -19,4 +19,4 @@
-
\ No newline at end of file +
From 126e5ff57a6a38a1ca41e60d5059c694d6c4c8c5 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 20:56:19 +0100 Subject: [PATCH 16/23] add debug log --- app/Jobs/GithubAppPermissionJob.php | 48 ++++++++++++++++++++++++++++- bootstrap/helpers/github.php | 41 +++++++++++++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/app/Jobs/GithubAppPermissionJob.php b/app/Jobs/GithubAppPermissionJob.php index 1ded6f455..89c3daa12 100644 --- a/app/Jobs/GithubAppPermissionJob.php +++ b/app/Jobs/GithubAppPermissionJob.php @@ -10,6 +10,7 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Log; class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue { @@ -26,21 +27,66 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue public function handle() { + Log::debug('Starting GithubAppPermissionJob', [ + 'app_id' => $this->github_app->app_id, + 'installation_id' => $this->github_app->installation_id, + 'api_url' => $this->github_app->api_url, + ]); + try { + Log::debug('Generating GitHub JWT token'); $github_access_token = generateGithubJwt($this->github_app); + + Log::debug('Fetching app permissions from GitHub API'); $response = Http::withHeaders([ 'Authorization' => "Bearer $github_access_token", - 'Accept' => 'application/vnd.github.machine-man-preview+json', + 'Accept' => 'application/vnd.github+json', ])->get("{$this->github_app->api_url}/app"); + + if (! $response->successful()) { + Log::error('GitHub API request failed', [ + 'status_code' => $response->status(), + 'error' => $response->body(), + 'app_id' => $this->github_app->app_id, + ]); + throw new \RuntimeException('Failed to fetch GitHub app permissions: '.$response->body()); + } + $response = $response->json(); $permissions = data_get($response, 'permissions'); + + Log::debug('Retrieved GitHub permissions', [ + 'app_id' => $this->github_app->app_id, + 'permissions' => $permissions, + ]); + $this->github_app->contents = data_get($permissions, 'contents'); $this->github_app->metadata = data_get($permissions, 'metadata'); $this->github_app->pull_requests = data_get($permissions, 'pull_requests'); $this->github_app->administration = data_get($permissions, 'administration'); + + Log::debug('Saving updated permissions to database', [ + 'app_id' => $this->github_app->app_id, + 'contents' => $this->github_app->contents, + 'metadata' => $this->github_app->metadata, + 'pull_requests' => $this->github_app->pull_requests, + 'administration' => $this->github_app->administration, + ]); + $this->github_app->save(); $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); + + Log::debug('Successfully completed GithubAppPermissionJob', [ + 'app_id' => $this->github_app->app_id, + ]); + } catch (\Throwable $e) { + Log::error('GithubAppPermissionJob failed', [ + 'app_id' => $this->github_app->app_id, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + send_internal_notification('GithubAppPermissionJob failed with: '.$e->getMessage()); throw $e; } diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index b687c0d68..c544ddcfc 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -5,6 +5,7 @@ use App\Models\GitlabApp; use Carbon\Carbon; use Carbon\CarbonImmutable; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; use Lcobucci\JWT\Encoding\ChainedFormatter; use Lcobucci\JWT\Encoding\JoseEncoder; @@ -14,12 +15,29 @@ use Lcobucci\JWT\Token\Builder; function generateGithubToken(GithubApp $source, string $type) { + Log::debug('Generating GitHub token', [ + 'app_id' => $source->app_id, + 'type' => $type, + 'api_url' => $source->api_url, + ]); + $response = Http::get("{$source->api_url}/zen"); $serverTime = CarbonImmutable::now()->setTimezone('UTC'); $githubTime = Carbon::parse($response->header('date')); $timeDiff = abs($serverTime->diffInSeconds($githubTime)); + Log::debug('Time synchronization check', [ + 'server_time' => $serverTime->format('Y-m-d H:i:s'), + 'github_time' => $githubTime->format('Y-m-d H:i:s'), + 'difference_seconds' => $timeDiff, + ]); + if ($timeDiff > 50) { + Log::error('System time out of sync with GitHub', [ + 'time_difference' => $timeDiff, + 'server_time' => $serverTime->format('Y-m-d H:i:s'), + 'github_time' => $githubTime->format('Y-m-d H:i:s'), + ]); throw new \Exception( 'System time is out of sync with GitHub API time:
'. '- System time: '.$serverTime->format('Y-m-d H:i:s').' UTC
'. @@ -41,18 +59,39 @@ function generateGithubToken(GithubApp $source, string $type) ->getToken($algorithm, $signingKey) ->toString(); + Log::debug('JWT token generated', [ + 'token_type' => $type, + 'issued_at' => $now->modify('-1 minute')->format('Y-m-d H:i:s'), + 'expires_at' => $now->modify('+8 minutes')->format('Y-m-d H:i:s'), + ]); + return match ($type) { 'jwt' => $jwt, 'installation' => (function () use ($source, $jwt) { + Log::debug('Requesting installation token', [ + 'app_id' => $source->app_id, + 'installation_id' => $source->installation_id, + ]); + $response = Http::withHeaders([ 'Authorization' => "Bearer $jwt", 'Accept' => 'application/vnd.github.machine-man-preview+json', ])->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens"); if (! $response->successful()) { - throw new RuntimeException("Failed to get installation token for {$source->name} with error: ".data_get($response->json(), 'message', 'no error message found')); + $error = data_get($response->json(), 'message', 'no error message found'); + Log::error('Failed to get installation token', [ + 'status_code' => $response->status(), + 'error_message' => $error, + 'app_id' => $source->app_id, + ]); + throw new RuntimeException("Failed to get installation token for {$source->name} with error: ".$error); } + Log::debug('Successfully obtained installation token', [ + 'app_id' => $source->app_id, + ]); + return $response->json()['token']; })(), default => throw new \InvalidArgumentException("Unsupported token type: {$type}") From deec885fd9899bdd0a350b68bca6c1c0be4b7e3d Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:08:22 +0100 Subject: [PATCH 17/23] fix: Expiration time claim ('exp') must be a numeric value --- bootstrap/helpers/github.php | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index c544ddcfc..be4ae65c5 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -51,6 +51,7 @@ function generateGithubToken(GithubApp $source, string $type) $algorithm = new Sha256; $tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default())); $now = CarbonImmutable::now()->setTimezone('UTC'); + $now = $now->setTime($now->format('H'), $now->format('i'), $now->format('s')); $jwt = $tokenBuilder ->issuedBy($source->app_id) From 09dfbde676e14e783db66b3b04cc1be59518312f Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:14:06 +0100 Subject: [PATCH 18/23] chore: remove debug log, finally found it --- app/Jobs/GithubAppPermissionJob.php | 25 ------------------------- bootstrap/helpers/github.php | 27 --------------------------- 2 files changed, 52 deletions(-) diff --git a/app/Jobs/GithubAppPermissionJob.php b/app/Jobs/GithubAppPermissionJob.php index 89c3daa12..d6da4439c 100644 --- a/app/Jobs/GithubAppPermissionJob.php +++ b/app/Jobs/GithubAppPermissionJob.php @@ -27,17 +27,9 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue public function handle() { - Log::debug('Starting GithubAppPermissionJob', [ - 'app_id' => $this->github_app->app_id, - 'installation_id' => $this->github_app->installation_id, - 'api_url' => $this->github_app->api_url, - ]); - try { - Log::debug('Generating GitHub JWT token'); $github_access_token = generateGithubJwt($this->github_app); - Log::debug('Fetching app permissions from GitHub API'); $response = Http::withHeaders([ 'Authorization' => "Bearer $github_access_token", 'Accept' => 'application/vnd.github+json', @@ -55,31 +47,14 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue $response = $response->json(); $permissions = data_get($response, 'permissions'); - Log::debug('Retrieved GitHub permissions', [ - 'app_id' => $this->github_app->app_id, - 'permissions' => $permissions, - ]); - $this->github_app->contents = data_get($permissions, 'contents'); $this->github_app->metadata = data_get($permissions, 'metadata'); $this->github_app->pull_requests = data_get($permissions, 'pull_requests'); $this->github_app->administration = data_get($permissions, 'administration'); - Log::debug('Saving updated permissions to database', [ - 'app_id' => $this->github_app->app_id, - 'contents' => $this->github_app->contents, - 'metadata' => $this->github_app->metadata, - 'pull_requests' => $this->github_app->pull_requests, - 'administration' => $this->github_app->administration, - ]); - $this->github_app->save(); $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); - Log::debug('Successfully completed GithubAppPermissionJob', [ - 'app_id' => $this->github_app->app_id, - ]); - } catch (\Throwable $e) { Log::error('GithubAppPermissionJob failed', [ 'app_id' => $this->github_app->app_id, diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index be4ae65c5..168308967 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -15,23 +15,11 @@ use Lcobucci\JWT\Token\Builder; function generateGithubToken(GithubApp $source, string $type) { - Log::debug('Generating GitHub token', [ - 'app_id' => $source->app_id, - 'type' => $type, - 'api_url' => $source->api_url, - ]); - $response = Http::get("{$source->api_url}/zen"); $serverTime = CarbonImmutable::now()->setTimezone('UTC'); $githubTime = Carbon::parse($response->header('date')); $timeDiff = abs($serverTime->diffInSeconds($githubTime)); - Log::debug('Time synchronization check', [ - 'server_time' => $serverTime->format('Y-m-d H:i:s'), - 'github_time' => $githubTime->format('Y-m-d H:i:s'), - 'difference_seconds' => $timeDiff, - ]); - if ($timeDiff > 50) { Log::error('System time out of sync with GitHub', [ 'time_difference' => $timeDiff, @@ -60,20 +48,9 @@ function generateGithubToken(GithubApp $source, string $type) ->getToken($algorithm, $signingKey) ->toString(); - Log::debug('JWT token generated', [ - 'token_type' => $type, - 'issued_at' => $now->modify('-1 minute')->format('Y-m-d H:i:s'), - 'expires_at' => $now->modify('+8 minutes')->format('Y-m-d H:i:s'), - ]); - return match ($type) { 'jwt' => $jwt, 'installation' => (function () use ($source, $jwt) { - Log::debug('Requesting installation token', [ - 'app_id' => $source->app_id, - 'installation_id' => $source->installation_id, - ]); - $response = Http::withHeaders([ 'Authorization' => "Bearer $jwt", 'Accept' => 'application/vnd.github.machine-man-preview+json', @@ -89,10 +66,6 @@ function generateGithubToken(GithubApp $source, string $type) throw new RuntimeException("Failed to get installation token for {$source->name} with error: ".$error); } - Log::debug('Successfully obtained installation token', [ - 'app_id' => $source->app_id, - ]); - return $response->json()['token']; })(), default => throw new \InvalidArgumentException("Unsupported token type: {$type}") From c789ed6003a4884cfb241fa1bca85fa0a155ed9f Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:16:57 +0100 Subject: [PATCH 19/23] chore: remove more logging --- app/Jobs/GithubAppPermissionJob.php | 12 ------------ bootstrap/helpers/github.php | 11 ----------- 2 files changed, 23 deletions(-) diff --git a/app/Jobs/GithubAppPermissionJob.php b/app/Jobs/GithubAppPermissionJob.php index d6da4439c..7cd1b86ac 100644 --- a/app/Jobs/GithubAppPermissionJob.php +++ b/app/Jobs/GithubAppPermissionJob.php @@ -10,7 +10,6 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Http; -use Illuminate\Support\Facades\Log; class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue { @@ -36,11 +35,6 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue ])->get("{$this->github_app->api_url}/app"); if (! $response->successful()) { - Log::error('GitHub API request failed', [ - 'status_code' => $response->status(), - 'error' => $response->body(), - 'app_id' => $this->github_app->app_id, - ]); throw new \RuntimeException('Failed to fetch GitHub app permissions: '.$response->body()); } @@ -56,12 +50,6 @@ class GithubAppPermissionJob implements ShouldBeEncrypted, ShouldQueue $this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret'); } catch (\Throwable $e) { - Log::error('GithubAppPermissionJob failed', [ - 'app_id' => $this->github_app->app_id, - 'error' => $e->getMessage(), - 'trace' => $e->getTraceAsString(), - ]); - send_internal_notification('GithubAppPermissionJob failed with: '.$e->getMessage()); throw $e; } diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php index 168308967..3a3f6e7b2 100644 --- a/bootstrap/helpers/github.php +++ b/bootstrap/helpers/github.php @@ -5,7 +5,6 @@ use App\Models\GitlabApp; use Carbon\Carbon; use Carbon\CarbonImmutable; use Illuminate\Support\Facades\Http; -use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; use Lcobucci\JWT\Encoding\ChainedFormatter; use Lcobucci\JWT\Encoding\JoseEncoder; @@ -21,11 +20,6 @@ function generateGithubToken(GithubApp $source, string $type) $timeDiff = abs($serverTime->diffInSeconds($githubTime)); if ($timeDiff > 50) { - Log::error('System time out of sync with GitHub', [ - 'time_difference' => $timeDiff, - 'server_time' => $serverTime->format('Y-m-d H:i:s'), - 'github_time' => $githubTime->format('Y-m-d H:i:s'), - ]); throw new \Exception( 'System time is out of sync with GitHub API time:
'. '- System time: '.$serverTime->format('Y-m-d H:i:s').' UTC
'. @@ -58,11 +52,6 @@ function generateGithubToken(GithubApp $source, string $type) if (! $response->successful()) { $error = data_get($response->json(), 'message', 'no error message found'); - Log::error('Failed to get installation token', [ - 'status_code' => $response->status(), - 'error_message' => $error, - 'app_id' => $source->app_id, - ]); throw new RuntimeException("Failed to get installation token for {$source->name} with error: ".$error); } From 70f93b151371cd3e76add2eed51142b0310f0718 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:38:21 +0100 Subject: [PATCH 20/23] fix: sanitize html error messages --- resources/views/errors/500.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index 10e9649d7..0ea295fe7 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -6,7 +6,7 @@

There has been an error with the following error message:

@if ($exception->getMessage() !== '')
- {!! $exception->getMessage() !!} + {!! Str::sanitize($exception->getMessage()) !!}
@endif
From 8a80401f156a473d6466cd16e378a89d9d4f9a0a Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:47:16 +0100 Subject: [PATCH 21/23] feat: Purify for better sanitization --- composer.json | 1 + composer.lock | 129 ++++++++++++++++++++++++++- config/purify.php | 115 ++++++++++++++++++++++++ resources/views/errors/500.blade.php | 2 +- 4 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 config/purify.php diff --git a/composer.json b/composer.json index 9bb2d034b..e680ac36c 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,7 @@ "spatie/laravel-ray": "^1.37", "spatie/laravel-schemaless-attributes": "^2.4", "spatie/url": "^2.2", + "stevebauman/purify": "^6.2", "stripe/stripe-php": "^16.2.0", "symfony/yaml": "^7.1.6", "visus/cuid2": "^4.1.0", diff --git a/composer.lock b/composer.lock index fe0aeafbc..821ee0cec 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "35041f9af7cbf8626f54891f1ede3622", + "content-hash": "aa98760c097f486cac380aa701e4317c", "packages": [ { "name": "3sidedcube/laravel-redoc", @@ -1883,6 +1883,67 @@ ], "time": "2024-12-27T00:36:43+00:00" }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.18.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "cb56001e54359df7ae76dc522d08845dc741621b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b", + "reference": "cb56001e54359df7ae76dc522d08845dc741621b", + "shasum": "" + }, + "require": { + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0" + }, + "time": "2024-11-01T03:51:45+00:00" + }, { "name": "firebase/php-jwt", "version": "v6.10.2", @@ -8330,6 +8391,72 @@ ], "time": "2024-03-08T11:35:19+00:00" }, + { + "name": "stevebauman/purify", + "version": "v6.2.2", + "source": { + "type": "git", + "url": "https://github.com/stevebauman/purify.git", + "reference": "a449299a3d5f5f8ef177e626721b3f69143890a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stevebauman/purify/zipball/a449299a3d5f5f8ef177e626721b3f69143890a4", + "reference": "a449299a3d5f5f8ef177e626721b3f69143890a4", + "shasum": "" + }, + "require": { + "ezyang/htmlpurifier": "^4.17", + "illuminate/contracts": "^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0", + "php": ">=7.4" + }, + "require-dev": { + "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0|^9.0", + "phpunit/phpunit": "^8.0|^9.0|^10.0" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Purify": "Stevebauman\\Purify\\Facades\\Purify" + }, + "providers": [ + "Stevebauman\\Purify\\PurifyServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Stevebauman\\Purify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Steve Bauman", + "email": "steven_bauman@outlook.com" + } + ], + "description": "An HTML Purifier / Sanitizer for Laravel", + "keywords": [ + "Purifier", + "clean", + "cleaner", + "html", + "laravel", + "purification", + "purify" + ], + "support": { + "issues": "https://github.com/stevebauman/purify/issues", + "source": "https://github.com/stevebauman/purify/tree/v6.2.2" + }, + "time": "2024-09-24T12:27:10+00:00" + }, { "name": "stripe/stripe-php", "version": "v16.4.0", diff --git a/config/purify.php b/config/purify.php new file mode 100644 index 000000000..66dbbb568 --- /dev/null +++ b/config/purify.php @@ -0,0 +1,115 @@ + 'default', + + /* + |-------------------------------------------------------------------------- + | Config sets + |-------------------------------------------------------------------------- + | + | Here you may configure various sets of configuration for differentiated use of HTMLPurifier. + | A specific set of configuration can be applied by calling the "config($name)" method on + | a Purify instance. Feel free to add/remove/customize these attributes as you wish. + | + | Documentation: http://htmlpurifier.org/live/configdoc/plain.html + | + | Core.Encoding The encoding to convert input to. + | HTML.Doctype Doctype to use during filtering. + | HTML.Allowed The allowed HTML Elements with their allowed attributes. + | HTML.ForbiddenElements The forbidden HTML elements. Elements that are listed in this + | string will be removed, however their content will remain. + | CSS.AllowedProperties The Allowed CSS properties. + | AutoFormat.AutoParagraph Newlines are converted in to paragraphs whenever possible. + | AutoFormat.RemoveEmpty Remove empty elements that contribute no semantic information to the document. + | + */ + + 'configs' => [ + + 'default' => [ + 'Core.Encoding' => 'utf-8', + 'HTML.Doctype' => 'HTML 4.01 Transitional', + 'HTML.Allowed' => 'h1,h2,h3,h4,h5,h6,b,u,strong,i,em,s,del,a[href|title],ul,ol,li,p[style],br,span,img[width|height|alt|src],blockquote', + 'HTML.ForbiddenElements' => '', + 'CSS.AllowedProperties' => 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align', + 'AutoFormat.AutoParagraph' => false, + 'AutoFormat.RemoveEmpty' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | HTMLPurifier definitions + |-------------------------------------------------------------------------- + | + | Here you may specify a class that augments the HTML definitions used by + | HTMLPurifier. Additional HTML5 definitions are provided out of the box. + | When specifying a custom class, make sure it implements the interface: + | + | \Stevebauman\Purify\Definitions\Definition + | + | Note that these definitions are applied to every Purifier instance. + | + | Documentation: http://htmlpurifier.org/docs/enduser-customize.html + | + */ + + 'definitions' => Html5Definition::class, + + /* + |-------------------------------------------------------------------------- + | HTMLPurifier CSS definitions + |-------------------------------------------------------------------------- + | + | Here you may specify a class that augments the CSS definitions used by + | HTMLPurifier. When specifying a custom class, make sure it implements + | the interface: + | + | \Stevebauman\Purify\Definitions\CssDefinition + | + | Note that these definitions are applied to every Purifier instance. + | + | CSS should be extending $definition->info['css-attribute'] = values + | See HTMLPurifier_CSSDefinition for further explanation + | + */ + + 'css-definitions' => null, + + /* + |-------------------------------------------------------------------------- + | Serializer + |-------------------------------------------------------------------------- + | + | The storage implementation where HTMLPurifier can store its serializer files. + | If the filesystem cache is in use, the path must be writable through the + | storage disk by the web server, otherwise an exception will be thrown. + | + */ + + 'serializer' => [ + 'driver' => env('CACHE_STORE', env('CACHE_DRIVER', 'file')), + 'cache' => \Stevebauman\Purify\Cache\CacheDefinitionCache::class, + ], + + // 'serializer' => [ + // 'disk' => env('FILESYSTEM_DISK', 'local'), + // 'path' => 'purify', + // 'cache' => \Stevebauman\Purify\Cache\FilesystemDefinitionCache::class, + // ], + +]; diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php index 0ea295fe7..cc672a324 100644 --- a/resources/views/errors/500.blade.php +++ b/resources/views/errors/500.blade.php @@ -6,7 +6,7 @@

There has been an error with the following error message:

@if ($exception->getMessage() !== '')
- {!! Str::sanitize($exception->getMessage()) !!} + {!! Purify::clean($exception->getMessage()) !!}
@endif
From 43d526b7735c451526af808ac0185c71372ccc71 Mon Sep 17 00:00:00 2001 From: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com> Date: Thu, 16 Jan 2025 18:06:46 +0100 Subject: [PATCH 22/23] fix: production password rule and cleanup code --- app/Providers/AppServiceProvider.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 6ed5e654e..c94574ce3 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -45,15 +45,14 @@ class AppServiceProvider extends ServiceProvider private function configurePasswords(): void { Password::defaults(function () { - $rule = Password::min(8)->letters(); - return App::isProduction() - ? $rule->mixedCase() + ? Password::min(8) + ->mixedCase() ->letters() ->numbers() ->symbols() ->uncompromised() - : $rule; + : Password::min(8)->letters(); }); } From dd897a11fd1f8b8fd724588b3aed2aeb0a89dd91 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 16 Jan 2025 21:07:12 +0100 Subject: [PATCH 23/23] disable model strict mode --- app/Providers/AppServiceProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index c94574ce3..329482230 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -39,7 +39,8 @@ class AppServiceProvider extends ServiceProvider private function configureModels(): void { - Model::shouldBeStrict(); + // Disabled because it's causing issues with the application + // Model::shouldBeStrict(); } private function configurePasswords(): void