diff --git a/app/Actions/Server/ValidateServer.php b/app/Actions/Server/ValidateServer.php
index 0a20deae5..22c48aa89 100644
--- a/app/Actions/Server/ValidateServer.php
+++ b/app/Actions/Server/ValidateServer.php
@@ -30,7 +30,8 @@ public function handle(Server $server)
]);
['uptime' => $this->uptime, 'error' => $error] = $server->validateConnection();
if (! $this->uptime) {
- $this->error = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.
Error: '.$error.'
';
+ $sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8');
+ $this->error = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.
Error: '.$sanitizedError.'
';
$server->update([
'validation_logs' => $this->error,
]);
diff --git a/app/Jobs/ValidateAndInstallServerJob.php b/app/Jobs/ValidateAndInstallServerJob.php
index 288904471..ee8cf2797 100644
--- a/app/Jobs/ValidateAndInstallServerJob.php
+++ b/app/Jobs/ValidateAndInstallServerJob.php
@@ -45,7 +45,8 @@ public function handle(): void
// Validate connection
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
if (! $uptime) {
- $errorMessage = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.
Error: '.$error;
+ $sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8');
+ $errorMessage = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.
Error: '.$sanitizedError;
$this->server->update([
'validation_logs' => $errorMessage,
'is_validating' => false,
@@ -197,7 +198,7 @@ public function handle(): void
]);
$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,
]);
}
diff --git a/app/Livewire/Server/PrivateKey/Show.php b/app/Livewire/Server/PrivateKey/Show.php
index fd55717fa..810b95ed4 100644
--- a/app/Livewire/Server/PrivateKey/Show.php
+++ b/app/Livewire/Server/PrivateKey/Show.php
@@ -63,7 +63,8 @@ public function checkConnection()
$this->dispatch('success', 'Server is reachable.');
$this->dispatch('refreshServerShow');
} else {
- $this->dispatch('error', 'Server is not reachable.
Check this documentation for further help.
Error: '.$error);
+ $sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8');
+ $this->dispatch('error', 'Server is not reachable.
Check this documentation for further help.
Error: '.$sanitizedError);
return;
}
diff --git a/app/Livewire/Server/ValidateAndInstall.php b/app/Livewire/Server/ValidateAndInstall.php
index 198d823b9..59ca4cd36 100644
--- a/app/Livewire/Server/ValidateAndInstall.php
+++ b/app/Livewire/Server/ValidateAndInstall.php
@@ -89,7 +89,8 @@ public function validateConnection()
$this->authorize('update', $this->server);
['uptime' => $this->uptime, 'error' => $error] = $this->server->validateConnection();
if (! $this->uptime) {
- $this->error = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.
Error: '.$error.'
';
+ $sanitizedError = htmlspecialchars($error ?? '', ENT_QUOTES, 'UTF-8');
+ $this->error = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.
Error: '.$sanitizedError.'
';
$this->server->update([
'validation_logs' => $this->error,
]);
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 9237763c8..00843b3da 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -269,6 +269,13 @@ public static function flushIdentityMap(): void
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()
{
return 'server';
diff --git a/config/purify.php b/config/purify.php
index 66dbbb568..a5dcabb92 100644
--- a/config/purify.php
+++ b/config/purify.php
@@ -49,6 +49,17 @@
'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'],
+ ],
+
],
/*
diff --git a/tests/Feature/ServerValidationXssTest.php b/tests/Feature/ServerValidationXssTest.php
new file mode 100644
index 000000000..ba8e6fcae
--- /dev/null
+++ b/tests/Feature/ServerValidationXssTest.php
@@ -0,0 +1,75 @@
+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 = '
';
+ $this->server->update(['validation_logs' => $xssPayload]);
+ $this->server->refresh();
+
+ expect($this->server->validation_logs)->not->toContain('
and($this->server->validation_logs)->not->toContain('onerror');
+});
+
+it('strips script tags from validation_logs', function () {
+ $xssPayload = '';
+ $this->server->update(['validation_logs' => $xssPayload]);
+ $this->server->refresh();
+
+ expect($this->server->validation_logs)->not->toContain('