Skip to content
Closed
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
2 changes: 2 additions & 0 deletions app/Controllers/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public function updateGeneral(Request $request, Response $response, mysqli $db):
'social_instagram' => trim((string) ($data['social_instagram'] ?? '')),
'social_linkedin' => trim((string) ($data['social_linkedin'] ?? '')),
'social_bluesky' => trim((string) ($data['social_bluesky'] ?? '')),
'social_telegram' => trim((string) ($data['social_telegram'] ?? '')),

@coderabbitai coderabbitai Bot Jul 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Validare e sanitizzare l’URL Telegram lato server.

type="url" è solo una validazione client-side: un POST arbitrario può salvare javascript: o altri schemi pericolosi, che diventano eseguibili quando il valore viene usato nel footer. Riusa HtmlHelper::sanitizePublicHttpUrl() e rifiuta valori non vuoti non validi prima di persisterli.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Controllers/SettingsController.php` at line 131, Aggiorna la gestione di
social_telegram in SettingsController per sanitizzare lato server l’URL tramite
HtmlHelper::sanitizePublicHttpUrl(). Mantieni vuoto il valore se l’input è
vuoto, ma rifiuta gli URL non validi o con schemi non HTTP(S) prima della
persistenza, evitando di salvare valori come javascript:.

Source: Path instructions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My change is stereotyped, the issue targets the existing code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Himura2la, corretto che il pattern esisteva già. Tuttavia social_telegram aggiunge un nuovo input persistito e renderizzato nel footer, quindi eredita direttamente la stessa vulnerabilità.

Per una correzione coerente, validerei tutti i link social nel loop con HtmlHelper::sanitizePublicHttpUrl() e rifiuterei qualsiasi valore non vuoto che non sia HTTP(S), invece di limitarsi a Telegram.

];

foreach ($socialLinks as $key => $value) {
Expand Down Expand Up @@ -254,6 +255,7 @@ private function resolveAppSettings(SettingsRepository $repository): array
'social_instagram' => $repository->get('app', 'social_instagram', $config['social_instagram'] ?? ''),
'social_linkedin' => $repository->get('app', 'social_linkedin', $config['social_linkedin'] ?? ''),
'social_bluesky' => $repository->get('app', 'social_bluesky', $config['social_bluesky'] ?? ''),
'social_telegram' => $repository->get('app', 'social_telegram', $config['social_telegram'] ?? ''),
];
}

Expand Down
3 changes: 2 additions & 1 deletion app/Support/ConfigStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public static function all(): array
'social_instagram' => '',
'social_linkedin' => '',
'social_bluesky' => '',
'social_telegram' => '',
],
'mail' => [
'driver' => 'mail', // mail|smtp|phpmailer
Expand Down Expand Up @@ -411,7 +412,7 @@ private static function loadDatabaseSettings(): array
self::$dbSettingsCache['app']['locale'] = (string) $raw['app']['locale'];
}
// Load social links
$socialKeys = ['social_facebook', 'social_twitter', 'social_instagram', 'social_linkedin', 'social_bluesky'];
$socialKeys = ['social_facebook', 'social_twitter', 'social_instagram', 'social_linkedin', 'social_bluesky', 'social_telegram'];
foreach ($socialKeys as $socialKey) {
if (isset($raw['app'][$socialKey])) {
self::$dbSettingsCache['app'][$socialKey] = (string) $raw['app'][$socialKey];
Expand Down
5 changes: 5 additions & 0 deletions app/Views/frontend/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
$socialInstagram = (string) ConfigStore::get('app.social_instagram', '');
$socialLinkedin = (string) ConfigStore::get('app.social_linkedin', '');
$socialBluesky = (string) ConfigStore::get('app.social_bluesky', '');
$socialTelegram = (string) ConfigStore::get('app.social_telegram', '');
$catalogRoute = route_path('catalog');
$reservationsRoute = route_path('reservations');
$wishlistRoute = route_path('wishlist');
Expand Down Expand Up @@ -1674,6 +1675,10 @@ class="fab fa-linkedin"></i></a>
<a href="<?= HtmlHelper::e($socialBluesky) ?>" target="_blank" rel="noopener noreferrer"><i
class="fa-brands fa-bluesky"></i></a>
<?php endif; ?>
<?php if ($socialTelegram !== ''): ?>
<a href="<?= HtmlHelper::e($socialTelegram) ?>" target="_blank" rel="noopener noreferrer"><i
class="fa-brands fa-telegram"></i></a>
<?php endif; ?>
Comment on lines +1678 to +1681

@coderabbitai coderabbitai Bot Jul 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Violazione delle direttive di progetto: non usare HtmlHelper::e() nelle view.

Entrambi i file utilizzano HtmlHelper::e() all'interno dell'attributo href del link Telegram. Come da istruzioni di progetto, l'uso di HtmlHelper::e() nelle view è severamente vietato e costituisce un rischio per la sicurezza (XSS). Occorre usare obbligatoriamente htmlspecialchars(..., ENT_QUOTES, 'UTF-8') per tutti gli attributi HTML.

  • app/Views/frontend/layout.php#L1678-L1681: sostituisci HtmlHelper::e($socialTelegram) con htmlspecialchars($socialTelegram, ENT_QUOTES, 'UTF-8').
  • app/Views/user_layout.php#L1067-L1070: sostituisci HtmlHelper::e($socialTelegram) con htmlspecialchars($socialTelegram, ENT_QUOTES, 'UTF-8').
🛡️ Modifica suggerita
-                            <a href="<?= HtmlHelper::e($socialTelegram) ?>" target="_blank" rel="noopener noreferrer"><i
+                            <a href="<?= htmlspecialchars($socialTelegram, ENT_QUOTES, 'UTF-8') ?>" target="_blank" rel="noopener noreferrer"><i
                                     class="fa-brands fa-telegram"></i></a>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<?php if ($socialTelegram !== ''): ?>
<a href="<?= HtmlHelper::e($socialTelegram) ?>" target="_blank" rel="noopener noreferrer"><i
class="fa-brands fa-telegram"></i></a>
<?php endif; ?>
<?php if ($socialTelegram !== ''): ?>
<a href="<?= htmlspecialchars($socialTelegram, ENT_QUOTES, 'UTF-8') ?>" target="_blank" rel="noopener noreferrer"><i
class="fa-brands fa-telegram"></i></a>
<?php endif; ?>
📍 Affects 2 files
  • app/Views/frontend/layout.php#L1678-L1681 (this comment)
  • app/Views/user_layout.php#L1067-L1070
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Views/frontend/layout.php` around lines 1678 - 1681, Replace
HtmlHelper::e($socialTelegram) with htmlspecialchars($socialTelegram,
ENT_QUOTES, 'UTF-8') in the Telegram href of app/Views/frontend/layout.php lines
1678-1681 and app/Views/user_layout.php lines 1067-1070; no other changes are
needed.

Source: Path instructions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My change is stereotyped, the issue targets the existing code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Himura2la Capisco: il pattern esisteva già. Tuttavia, le due occorrenze di Telegram sono nuove in questa PR e ricadono nella regola esplicita per app/Views/**.

La richiesta resta quindi limitata ai nuovi link Telegram; non richiede di modificare i link social preesistenti.

</div>
</div>
</div>
Expand Down
11 changes: 11 additions & 0 deletions app/Views/settings/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,17 @@ class="block w-full rounded-lg border-gray-300 focus:border-gray-500 focus:ring-
class="block w-full rounded-lg border-gray-300 focus:border-gray-500 focus:ring-gray-500 text-sm py-2 px-3"
placeholder="<?= __('https://bsky.app/profile/tuoprofilo') ?>">
</div>
<div>
<label for="social_telegram" class="block text-xs text-gray-600 mb-1">
<i class="fa-brands fa-telegram mr-1"></i> Telegram
</label>
<input type="url"
id="social_telegram"
name="social_telegram"
value="<?php echo HtmlHelper::e((string)($appSettings['social_telegram'] ?? '')); ?>"
class="block w-full rounded-lg border-gray-300 focus:border-gray-500 focus:ring-gray-500 text-sm py-2 px-3"
placeholder="<?= __('https://t.me/tuocanale') ?>">
</div>
</div>
<p class="text-xs text-gray-500 mt-2">
<i class="fas fa-info-circle"></i> <?= __("Lascia vuoto per nascondere il social dal footer") ?>
Expand Down
5 changes: 5 additions & 0 deletions app/Views/user_layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
$socialInstagram = (string) ConfigStore::get('app.social_instagram', '');
$socialLinkedin = (string) ConfigStore::get('app.social_linkedin', '');
$socialBluesky = (string) ConfigStore::get('app.social_bluesky', '');
$socialTelegram = (string) ConfigStore::get('app.social_telegram', '');
$catalogRoute = route_path('catalog');
$reservationsRoute = route_path('reservations');
$wishlistRoute = route_path('wishlist');
Expand Down Expand Up @@ -1063,6 +1064,10 @@ class="fab fa-linkedin"></i></a>
<a href="<?= HtmlHelper::e($socialBluesky) ?>" target="_blank" rel="noopener noreferrer"><i
class="fa-brands fa-bluesky"></i></a>
<?php endif; ?>
<?php if ($socialTelegram !== ''): ?>
<a href="<?= HtmlHelper::e($socialTelegram) ?>" target="_blank" rel="noopener noreferrer"><i
class="fa-brands fa-telegram"></i></a>
<?php endif; ?>
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/settings.MD
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ La pagina Impostazioni (`/admin/settings`) è organizzata in **11 tab**. Il tab

| Tab (`?tab=`) | Contenuto |
|---|---|
| `general` | Identità: nome, logo, descrizione footer, link social (Facebook, Twitter/X, Instagram, LinkedIn, Bluesky) |
| `general` | Identità: nome, logo, descrizione footer, link social (Facebook, Twitter/X, Instagram, LinkedIn, Bluesky, Telegram) |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| `email` | Provider/SMTP + toggle "richiedi approvazione admin" alla registrazione |
| `templates` | Template email (19 tipi) |
| `cms` | Contenuti homepage / pagine + layout immagine eventi |
Expand Down
1 change: 1 addition & 0 deletions installer/database/data_de_DE.sql
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `desc
('app', 'social_instagram', '', 'Instagram-Profil-URL', NOW()),
('app', 'social_linkedin', '', 'LinkedIn-Profil-URL', NOW()),
('app', 'social_bluesky', '', 'Bluesky-Profil-URL', NOW()),
('app', 'social_telegram', '', 'Telegram-Profil-URL', NOW()),

-- Email settings are configured by user in installer step 6
-- No default values here to avoid overwriting user choices
Expand Down
1 change: 1 addition & 0 deletions installer/database/data_en_US.sql
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `desc
('app', 'social_instagram', '', 'Instagram profile URL', NOW()),
('app', 'social_linkedin', '', 'LinkedIn profile URL', NOW()),
('app', 'social_bluesky', '', 'Bluesky profile URL', NOW()),
('app', 'social_telegram', '', 'Telegram profile URL', NOW()),

-- Email settings are configured by user in installer step 6
-- No default values here to avoid overwriting user choices
Expand Down
1 change: 1 addition & 0 deletions installer/database/data_fr_FR.sql
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `desc
('app', 'social_instagram', '', 'URL du profil Instagram', NOW()),
('app', 'social_linkedin', '', 'URL du profil LinkedIn', NOW()),
('app', 'social_bluesky', '', 'URL du profil Bluesky', NOW()),
('app', 'social_telegram', '', 'URL du profil Telegram', NOW()),

-- Email settings are configured by user in installer step 6
-- No default values here to avoid overwriting user choices
Expand Down
1 change: 1 addition & 0 deletions installer/database/data_it_IT.sql
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ INSERT INTO `system_settings` (`category`, `setting_key`, `setting_value`, `desc
('app', 'social_instagram', '', 'Instagram profile URL', NOW()),
('app', 'social_linkedin', '', 'LinkedIn profile URL', NOW()),
('app', 'social_bluesky', '', 'Bluesky profile URL', NOW()),
('app', 'social_telegram', '', 'Telegram profile URL', NOW()),
Comment thread
coderabbitai[bot] marked this conversation as resolved.

-- Le impostazioni email sono configurate dall'utente nello step 6 dell'installer
-- Nessun valore predefinito qui per evitare di sovrascrivere le scelte dell'utente
Expand Down
9 changes: 9 additions & 0 deletions installer/database/migrations/migrate_0.7.35.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Migration 0.7.35 — add Telegram social link setting.
--
-- Existing installations upgraded to 0.7.35 must get app.social_telegram
-- in system_settings so the new admin/footer integration can persist/read it.
-- INSERT IGNORE keeps this idempotent on reruns and on fresh installs seeded
-- from installer/database/data_*.sql.

INSERT IGNORE INTO `system_settings` (`category`, `setting_key`, `setting_value`, `description`) VALUES
('app', 'social_telegram', '', 'Telegram profile URL');
69 changes: 69 additions & 0 deletions tests/issue-257-telegram-social-links.unit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);

/**
* Static coverage for issue #257 (Telegram social link).
*
* Run:
* php tests/issue-257-telegram-social-links.unit.php
*/

$root = dirname(__DIR__);
$failed = 0;
$passed = 0;

$check = static function (bool $cond, string $label) use (&$failed, &$passed): void {
if ($cond) {
$passed++;
echo " OK {$label}\n";
return;
}
$failed++;
echo " FAIL {$label}\n";
};

$read = static function (string $rel) use ($root): string {
$path = $root . '/' . $rel;
if (!is_file($path)) {
throw new RuntimeException("Missing file: {$rel}");
}
$content = file_get_contents($path);
if ($content === false) {
throw new RuntimeException("Unreadable file: {$rel}");
}
return $content;
};

try {
$configStore = $read('app/Support/ConfigStore.php');
$settingsController = $read('app/Controllers/SettingsController.php');
$settingsView = $read('app/Views/settings/index.php');
$frontendLayout = $read('app/Views/frontend/layout.php');
$userLayout = $read('app/Views/user_layout.php');
$migration = $read('installer/database/migrations/migrate_0.7.35.sql');
$seedEn = $read('installer/database/data_en_US.sql');
$seedDe = $read('installer/database/data_de_DE.sql');
$seedFr = $read('installer/database/data_fr_FR.sql');
$seedIt = $read('installer/database/data_it_IT.sql');

$check(str_contains($configStore, "'social_telegram' => ''"), 'ConfigStore default includes social_telegram');
$check(str_contains($settingsController, "'social_telegram' => trim((string) (\$data['social_telegram'] ?? ''))"), 'SettingsController saves social_telegram');
$check(str_contains($settingsController, "'social_telegram' => \$repository->get('app', 'social_telegram', \$config['social_telegram'] ?? '')"), 'SettingsController resolves social_telegram');
$check(str_contains($settingsView, 'id="social_telegram"'), 'Settings view has social_telegram input');
$check(str_contains($settingsView, 'class="fa-brands fa-telegram mr-1"'), 'Settings view uses Telegram icon');
$check(str_contains($frontendLayout, "ConfigStore::get('app.social_telegram', '')"), 'Frontend layout loads social_telegram');
$check(str_contains($frontendLayout, 'class="fa-brands fa-telegram"'), 'Frontend layout renders Telegram icon');
$check(str_contains($userLayout, "ConfigStore::get('app.social_telegram', '')"), 'User layout loads social_telegram');
$check(str_contains($userLayout, 'class="fa-brands fa-telegram"'), 'User layout renders Telegram icon');
$check(str_contains($migration, "'app', 'social_telegram', '', 'Telegram profile URL'"), 'Migration inserts social_telegram');
$check(str_contains($seedEn, "('app', 'social_telegram', '', 'Telegram profile URL', NOW())"), 'en_US seed includes social_telegram');
$check(str_contains($seedDe, "('app', 'social_telegram', '', 'Telegram-Profil-URL', NOW())"), 'de_DE seed includes social_telegram');
$check(str_contains($seedFr, "('app', 'social_telegram', '', 'URL du profil Telegram', NOW())"), 'fr_FR seed includes social_telegram');
$check(str_contains($seedIt, "('app', 'social_telegram', '', 'Telegram profile URL', NOW())"), 'it_IT seed includes social_telegram');
} catch (Throwable $e) {
$failed++;
echo " FAIL exception: " . $e->getMessage() . "\n";
}

echo "\n {$passed} passed, {$failed} failed\n";
exit($failed === 0 ? 0 : 1);
Loading