fix: sanitize error output in server validation logs (#9197)
This commit is contained in:
commit
25dcde6a47
7 changed files with 102 additions and 5 deletions
|
|
@ -30,7 +30,8 @@ public function handle(Server $server)
|
||||||
]);
|
]);
|
||||||
['uptime' => $this->uptime, 'error' => $error] = $server->validateConnection();
|
['uptime' => $this->uptime, 'error' => $error] = $server->validateConnection();
|
||||||
if (! $this->uptime) {
|
if (! $this->uptime) {
|
||||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br>Check this <a target="_blank" class="text-black underline dark:text-white" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br><div class="text-error">Error: '.$error.'</div>';
|
$sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8');
|
||||||
|
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br>Check this <a target="_blank" class="text-black underline dark:text-white" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br><div class="text-error">Error: '.$sanitizedError.'</div>';
|
||||||
$server->update([
|
$server->update([
|
||||||
'validation_logs' => $this->error,
|
'validation_logs' => $this->error,
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,8 @@ public function handle(): void
|
||||||
// Validate connection
|
// Validate connection
|
||||||
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
||||||
if (! $uptime) {
|
if (! $uptime) {
|
||||||
$errorMessage = 'Server is not reachable. Please validate your configuration and connection.<br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: '.$error;
|
$sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8');
|
||||||
|
$errorMessage = 'Server is not reachable. Please validate your configuration and connection.<br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: '.$sanitizedError;
|
||||||
$this->server->update([
|
$this->server->update([
|
||||||
'validation_logs' => $errorMessage,
|
'validation_logs' => $errorMessage,
|
||||||
'is_validating' => false,
|
'is_validating' => false,
|
||||||
|
|
@ -197,7 +198,7 @@ public function handle(): void
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->server->update([
|
$this->server->update([
|
||||||
'validation_logs' => 'An error occurred during validation: '.$e->getMessage(),
|
'validation_logs' => 'An error occurred during validation: '.htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'),
|
||||||
'is_validating' => false,
|
'is_validating' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,8 @@ public function checkConnection()
|
||||||
$this->dispatch('success', 'Server is reachable.');
|
$this->dispatch('success', 'Server is reachable.');
|
||||||
$this->dispatch('refreshServerShow');
|
$this->dispatch('refreshServerShow');
|
||||||
} else {
|
} else {
|
||||||
$this->dispatch('error', 'Server is not reachable.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.<br><br>Error: '.$error);
|
$sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8');
|
||||||
|
$this->dispatch('error', 'Server is not reachable.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help.<br><br>Error: '.$sanitizedError);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ public function validateConnection()
|
||||||
$this->authorize('update', $this->server);
|
$this->authorize('update', $this->server);
|
||||||
['uptime' => $this->uptime, 'error' => $error] = $this->server->validateConnection();
|
['uptime' => $this->uptime, 'error' => $error] = $this->server->validateConnection();
|
||||||
if (! $this->uptime) {
|
if (! $this->uptime) {
|
||||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br>Check this <a target="_blank" class="text-black underline dark:text-white" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br><div class="text-error">Error: '.$error.'</div>';
|
$sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8');
|
||||||
|
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br>Check this <a target="_blank" class="text-black underline dark:text-white" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br><div class="text-error">Error: '.$sanitizedError.'</div>';
|
||||||
$this->server->update([
|
$this->server->update([
|
||||||
'validation_logs' => $this->error,
|
'validation_logs' => $this->error,
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -269,6 +269,13 @@ public static function flushIdentityMap(): void
|
||||||
|
|
||||||
use HasSafeStringAttribute;
|
use HasSafeStringAttribute;
|
||||||
|
|
||||||
|
public function setValidationLogsAttribute($value): void
|
||||||
|
{
|
||||||
|
$this->attributes['validation_logs'] = $value !== null
|
||||||
|
? \Stevebauman\Purify\Facades\Purify::config('validation_logs')->clean($value)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
public function type()
|
public function type()
|
||||||
{
|
{
|
||||||
return 'server';
|
return 'server';
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,17 @@
|
||||||
'AutoFormat.RemoveEmpty' => false,
|
'AutoFormat.RemoveEmpty' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'validation_logs' => [
|
||||||
|
'Core.Encoding' => 'utf-8',
|
||||||
|
'HTML.Doctype' => 'HTML 4.01 Transitional',
|
||||||
|
'HTML.Allowed' => 'a[href|title|target|class],br,div[class],pre[class],span[class],p[class]',
|
||||||
|
'HTML.ForbiddenElements' => '',
|
||||||
|
'CSS.AllowedProperties' => '',
|
||||||
|
'AutoFormat.AutoParagraph' => false,
|
||||||
|
'AutoFormat.RemoveEmpty' => false,
|
||||||
|
'Attr.AllowedFrameTargets' => ['_blank'],
|
||||||
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
75
tests/Feature/ServerValidationXssTest.php
Normal file
75
tests/Feature/ServerValidationXssTest.php
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$this->team = Team::factory()->create();
|
||||||
|
$user->teams()->attach($this->team);
|
||||||
|
$this->actingAs($user);
|
||||||
|
session(['currentTeam' => $this->team]);
|
||||||
|
|
||||||
|
$this->server = Server::factory()->create([
|
||||||
|
'team_id' => $this->team->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('strips dangerous HTML from validation_logs via mutator', function () {
|
||||||
|
$xssPayload = '<img src=x onerror=alert(document.domain)>';
|
||||||
|
$this->server->update(['validation_logs' => $xssPayload]);
|
||||||
|
$this->server->refresh();
|
||||||
|
|
||||||
|
expect($this->server->validation_logs)->not->toContain('<img')
|
||||||
|
->and($this->server->validation_logs)->not->toContain('onerror');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('strips script tags from validation_logs', function () {
|
||||||
|
$xssPayload = '<script>alert("xss")</script>';
|
||||||
|
$this->server->update(['validation_logs' => $xssPayload]);
|
||||||
|
$this->server->refresh();
|
||||||
|
|
||||||
|
expect($this->server->validation_logs)->not->toContain('<script');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves allowed HTML in validation_logs', function () {
|
||||||
|
$allowedHtml = 'Server is not reachable.<br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs">documentation</a> for further help.<br><br><div class="text-error">Error: Connection refused</div>';
|
||||||
|
$this->server->update(['validation_logs' => $allowedHtml]);
|
||||||
|
$this->server->refresh();
|
||||||
|
|
||||||
|
expect($this->server->validation_logs)->toContain('<a')
|
||||||
|
->and($this->server->validation_logs)->toContain('<br')
|
||||||
|
->and($this->server->validation_logs)->toContain('<div')
|
||||||
|
->and($this->server->validation_logs)->toContain('Connection refused');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows null validation_logs', function () {
|
||||||
|
$this->server->update(['validation_logs' => null]);
|
||||||
|
$this->server->refresh();
|
||||||
|
|
||||||
|
expect($this->server->validation_logs)->toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sanitizes XSS embedded within valid error HTML', function () {
|
||||||
|
$maliciousError = 'Server is not reachable.<br><div class="text-error">Error: <img src=x onerror=alert(document.cookie)></div>';
|
||||||
|
$this->server->update(['validation_logs' => $maliciousError]);
|
||||||
|
$this->server->refresh();
|
||||||
|
|
||||||
|
expect($this->server->validation_logs)->toContain('<div')
|
||||||
|
->and($this->server->validation_logs)->toContain('Error:')
|
||||||
|
->and($this->server->validation_logs)->not->toContain('onerror')
|
||||||
|
->and($this->server->validation_logs)->not->toContain('<img');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sanitizes event handler attributes in validation_logs', function () {
|
||||||
|
$payload = '<div onmouseover="alert(1)" class="text-error">Error</div>';
|
||||||
|
$this->server->update(['validation_logs' => $payload]);
|
||||||
|
$this->server->refresh();
|
||||||
|
|
||||||
|
expect($this->server->validation_logs)->toContain('<div')
|
||||||
|
->and($this->server->validation_logs)->not->toContain('onmouseover');
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue