Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/Controllers/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ private function resolveAdvancedSettings(SettingsRepository $repository): array
'custom_js_marketing' => ContentSanitizer::normalizeExternalAssets(
$repository->get('advanced', 'custom_js_marketing', $config['custom_js_marketing'] ?? '')
),
'custom_header_css' => ContentSanitizer::normalizeExternalAssets(
'custom_header_css' => ContentSanitizer::sanitizeCustomCss(
$repository->get('advanced', 'custom_header_css', $config['custom_header_css'] ?? '')
),
'days_before_expiry_warning' => (int) $repository->get('advanced', 'days_before_expiry_warning', (string) ($config['days_before_expiry_warning'] ?? 3)),
Expand Down Expand Up @@ -852,7 +852,7 @@ public function updateAdvancedSettings(Request $request, Response $response, mys
'custom_js_essential' => ContentSanitizer::normalizeExternalAssets(trim((string) ($data['custom_js_essential'] ?? ''))),
'custom_js_analytics' => ContentSanitizer::normalizeExternalAssets(trim((string) ($data['custom_js_analytics'] ?? ''))),
'custom_js_marketing' => ContentSanitizer::normalizeExternalAssets(trim((string) ($data['custom_js_marketing'] ?? ''))),
'custom_header_css' => ContentSanitizer::normalizeExternalAssets(trim((string) ($data['custom_header_css'] ?? ''))),
'custom_header_css' => ContentSanitizer::sanitizeCustomCss(trim((string) ($data['custom_header_css'] ?? ''))),
'days_before_expiry_warning' => (string) $daysBeforeWarning,
'session_lifetime' => (string) $sessionLifetime,
'force_https' => isset($data['force_https']) && $data['force_https'] === '1' ? '1' : '0',
Expand Down
5 changes: 3 additions & 2 deletions app/Controllers/ThemeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,9 @@ public function save(Request $request, Response $response, array $args): Respons
'custom_js' => $parsedBody['advanced']['custom_js'] ?? ''
];

// Sanitize custom CSS/JS (basic sanitization - remove <script> tags from CSS)
$advanced['custom_css'] = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $advanced['custom_css']);
// Neutralise any </style>/<script> breakout in the CSS so it can
// never escape its inline <style> wrapper into script execution.
$advanced['custom_css'] = \App\Support\ContentSanitizer::sanitizeCustomCss((string) $advanced['custom_css']);

$this->themeManager->updateAdvancedSettings($themeId, $advanced);
}
Expand Down
30 changes: 30 additions & 0 deletions app/Support/ContentSanitizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,34 @@ public static function normalizeExternalAssets(string $content): string

return preg_replace($patterns, '', $content) ?? $content;
}

/**
* Sanitizza CSS destinato a un blocco inline <style>…</style>.
*
* Il contenuto di un <style> è "raw text" per il parser HTML: l'unico
* modo per uscirne ed arrivare all'esecuzione di script è il tag di
* chiusura `</style` (ASCII case-insensitive). Un CSS valido non contiene
* mai quella sequenza né `<script`, quindi neutralizzarle chiude il
* vettore di breakout→XSS senza toccare alcun foglio di stile reale.
* Applicata SIA in salvataggio (i valori stored restano puliti) SIA in
* rendering (protegge i valori già presenti nel DB o iniettati altrove).
*/
public static function sanitizeCustomCss(string $css): string
{
if ($css === '') {
return $css;
}

// Prima la normalizzazione degli asset esterni (Google Fonts → HTTPS).
$css = self::normalizeExternalAssets($css);

// Rimuove ogni apertura/chiusura di <style>/<script>: sequenze che
// non compaiono mai in CSS legittimo ma che consentirebbero di
// rompere il contesto raw-text ed eseguire JavaScript.
$css = preg_replace('#<\s*/?\s*(?:style|script)\b[^>]*>?#i', '', $css) ?? $css;

// Difesa aggiuntiva: annulla i marcatori di commento HTML che
// potrebbero mascherare un tag in scenari di parsing anomali.
return str_ireplace(['<!--', '-->', '<![cdata[', ']]>'], '', $css);
}
}
2 changes: 1 addition & 1 deletion app/Views/frontend/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,7 @@
<?php
// Load custom CSS from settings
$customCss = ConfigStore::get('advanced.custom_header_css', '');
$customCss = is_string($customCss) ? ContentSanitizer::normalizeExternalAssets($customCss) : $customCss;
$customCss = is_string($customCss) ? ContentSanitizer::sanitizeCustomCss($customCss) : $customCss;
if (!empty($customCss)):
?>
<style>
Expand Down
15 changes: 7 additions & 8 deletions installer/database/data_de_DE.sql
Original file line number Diff line number Diff line change
Expand Up @@ -188,31 +188,31 @@ INSERT IGNORE INTO `generi` VALUES (181,'Oratorium',NULL,'2025-10-20 16:20:00','

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('cms', 'events_page_enabled', '1', 'Veranstaltungsseite im Frontend aktivieren oder deaktivieren')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('system', 'catalogue_mode', '0', 'Nur-Katalog-Modus - deaktiviert Ausleihen, Vormerkungen und Wunschliste')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'pickup_expiry_days', '3', 'Tage zum Abholen einer genehmigten Ausleihe bevor sie verfällt')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'loan_duration_days', '30', 'Standardausleihfrist in Tagen')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_active_loans_per_user', '0', 'Maximale Anzahl aktiver Ausleihen pro Benutzer (0 = kein Limit)')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_renewals', '3', 'Maximale Anzahl erlaubter Verlängerungen pro Ausleihe')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_loan_duration_days', '90', 'Maximal anforderbare Ausleihdauer in Tagen')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

-- ============================================================================
-- System Settings - Complete default configuration
Expand Down Expand Up @@ -298,7 +298,6 @@ INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `desc
-- Social sharing settings
('sharing', 'enabled_providers', 'facebook,x,whatsapp,email', 'Aktivierte Social-Sharing-Anbieter auf der Buchdetailseite', NOW())
ON DUPLICATE KEY UPDATE
setting_value = VALUES(setting_value),
description = VALUES(description),
updated_at = NOW();

Expand Down
17 changes: 9 additions & 8 deletions installer/database/data_en_US.sql
Original file line number Diff line number Diff line change
Expand Up @@ -187,30 +187,32 @@ INSERT INTO `generi` VALUES (180,'Music drama',NULL,'2025-10-20 16:20:00','2025-
INSERT INTO `generi` VALUES (181,'Oratorio',NULL,'2025-10-20 16:20:00','2025-10-20 16:20:00',11);

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('cms', 'events_page_enabled', '1', 'Enable or disable the events page on the frontend');
('cms', 'events_page_enabled', '1', 'Enable or disable the events page on the frontend')
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('system', 'catalogue_mode', '0', 'Catalogue-only mode - disables loans, reservations and wishlist');
('system', 'catalogue_mode', '0', 'Catalogue-only mode - disables loans, reservations and wishlist')
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'pickup_expiry_days', '3', 'Days to pick up an approved loan before it expires')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'loan_duration_days', '30', 'Default loan duration in days')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_active_loans_per_user', '0', 'Maximum active loans per user (0 = no limit)')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_renewals', '3', 'Maximum number of renewals allowed per loan')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_loan_duration_days', '90', 'Maximum requestable loan duration in days')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

-- ============================================================================
-- System Settings - Complete default configuration
Expand Down Expand Up @@ -296,7 +298,6 @@ INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `desc
-- Social sharing settings
('sharing', 'enabled_providers', 'facebook,x,whatsapp,email', 'Enabled social sharing providers on book detail page', NOW())
ON DUPLICATE KEY UPDATE
setting_value = VALUES(setting_value),
description = VALUES(description),
updated_at = NOW();

Expand Down
15 changes: 7 additions & 8 deletions installer/database/data_fr_FR.sql
Original file line number Diff line number Diff line change
Expand Up @@ -188,31 +188,31 @@ INSERT IGNORE INTO `generi` VALUES (181,'Oratorio',NULL,'2025-10-20 16:20:00','2

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('cms', 'events_page_enabled', '1', 'Activer ou désactiver la page événements dans le frontend')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('system', 'catalogue_mode', '0', 'Mode catalogue uniquement - désactive les emprunts, réservations et liste de souhaits')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'pickup_expiry_days', '3', 'Jours pour récupérer un emprunt approuvé avant expiration')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'loan_duration_days', '30', 'Durée d\'emprunt par défaut en jours')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_active_loans_per_user', '0', 'Nombre maximal d\'emprunts actifs par utilisateur (0 = aucune limite)')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_renewals', '3', 'Nombre maximal de renouvellements autorisés par emprunt')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_loan_duration_days', '90', 'Durée maximale demandable pour un emprunt en jours')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

-- ============================================================================
-- System Settings - Complete default configuration
Expand Down Expand Up @@ -298,7 +298,6 @@ INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `desc
-- Social sharing settings
('sharing', 'enabled_providers', 'facebook,x,whatsapp,email', 'Fournisseurs de partage social activés sur la page de détail du livre', NOW())
ON DUPLICATE KEY UPDATE
setting_value = VALUES(setting_value),
description = VALUES(description),
updated_at = NOW();

Expand Down
17 changes: 9 additions & 8 deletions installer/database/data_it_IT.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,32 @@
SET FOREIGN_KEY_CHECKS=0;

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('cms', 'events_page_enabled', '1', 'Abilita o disabilita la pagina degli eventi nel frontend');
('cms', 'events_page_enabled', '1', 'Abilita o disabilita la pagina degli eventi nel frontend')
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('system', 'catalogue_mode', '0', 'Modalità solo catalogo - disabilita prestiti, prenotazioni e wishlist');
('system', 'catalogue_mode', '0', 'Modalità solo catalogo - disabilita prestiti, prenotazioni e wishlist')
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'pickup_expiry_days', '3', 'Giorni per ritirare un prestito approvato prima che scada')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'loan_duration_days', '30', 'Durata predefinita di un prestito in giorni')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_active_loans_per_user', '0', 'Numero massimo di prestiti attivi per utente (0 = nessun limite)')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_renewals', '3', 'Numero massimo di rinnovi consentiti per prestito')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('loans', 'max_loan_duration_days', '90', 'Durata massima richiedibile per un prestito in giorni')
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), description = VALUES(description), updated_at = NOW();
ON DUPLICATE KEY UPDATE description = VALUES(description), updated_at = NOW();

INSERT INTO `generi` VALUES (1,'Prosa',NULL,'2025-10-20 16:20:00','2025-10-20 16:20:00',NULL);
INSERT INTO `generi` VALUES (2,'Poesia',NULL,'2025-10-20 16:20:00','2025-10-20 16:20:00',NULL);
Expand Down Expand Up @@ -349,7 +351,6 @@ INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `desc
-- Social sharing settings
('sharing', 'enabled_providers', 'facebook,x,whatsapp,email', 'Provider di condivisione social abilitati nella pagina dettaglio libro', NOW())
ON DUPLICATE KEY UPDATE
setting_value = VALUES(setting_value),
description = VALUES(description),
updated_at = NOW();

Expand Down
Loading
Loading