fix: obfuscate email in contact links, raise rate limits, make Libre toggleable

- about.php: use EmailObfuscator::email() for contact email link text instead of htmlspecialchars
- SearchController: raise rate limit from 30 to 300 req/min
- request-access.php: raise rate limit from 3 to 30 req/10min
- partage/index.php: raise rate limit from 5 to 50 req/10min
- contenus.php: make Libre option toggleable (remove disabled class), move to top of Degré d'ouverture, remove temporary note about next academic year
This commit is contained in:
Pontoporeia
2026-05-10 23:40:27 +02:00
parent 2bacc78492
commit 48da914bc8
7 changed files with 29 additions and 14 deletions

View File

@@ -1,5 +1,8 @@
# TODO # TODO
- [x] Fix email addresses in about.php contacts section not using EmailObfuscator for link text
- [x] Raise rate limits: SearchController 30→300, request-access 3→30, partage 5→50
- [x] Make Libre option toggleable in Degré d'ouverture fieldset, move to top, remove temporary note
- [x] Improve recapitulatif.php (partage): bottom margin/padding, center .thanks-success - [x] Improve recapitulatif.php (partage): bottom margin/padding, center .thanks-success
- [x] Display ALL submitted info in recapitulatif page + email recap - [x] Display ALL submitted info in recapitulatif page + email recap
- [x] Add "validate your info / contact xamxam@erg.be" note on recap page - [x] Add "validate your info / contact xamxam@erg.be" note on recap page

View File

@@ -471,7 +471,7 @@ function handleShareLinkSubmission(string $slug): void
// 5 submissions per IP per 10 minutes, keyed per share link. // 5 submissions per IP per 10 minutes, keyed per share link.
$rateLimitCacheDir = STORAGE_ROOT . '/cache/rate_limit'; $rateLimitCacheDir = STORAGE_ROOT . '/cache/rate_limit';
$shareRateLimitId = 'share_' . $slug . '_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'); $shareRateLimitId = 'share_' . $slug . '_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
$rateLimit = new RateLimit(5, 600, $rateLimitCacheDir); $rateLimit = new RateLimit(50, 600, $rateLimitCacheDir);
if (!$rateLimit->checkKey($shareRateLimitId)) { if (!$rateLimit->checkKey($shareRateLimitId)) {
$_SESSION['_flash_error'] = 'Trop de tentatives. Veuillez réessayer plus tard.'; $_SESSION['_flash_error'] = 'Trop de tentatives. Veuillez réessayer plus tard.';

View File

@@ -88,7 +88,7 @@ if ($accessTypeId !== 2) {
// Rate limiting: max 3 requests per 10 minutes per IP // Rate limiting: max 3 requests per 10 minutes per IP
$rateLimitKey = 'access_request_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'); $rateLimitKey = 'access_request_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
if (!(new RateLimit(3, 600))->checkKey($rateLimitKey)) { if (!(new RateLimit(30, 600))->checkKey($rateLimitKey)) {
http_response_code(429); http_response_code(429);
echo json_encode(['success' => false, 'message' => 'Trop de requêtes. Veuillez réessayer dans quelques minutes.']); echo json_encode(['success' => false, 'message' => 'Trop de requêtes. Veuillez réessayer dans quelques minutes.']);
exit; exit;

View File

@@ -22,7 +22,7 @@
*/ */
class SearchController class SearchController
{ {
private const RATE_LIMIT_MAX = 30; private const RATE_LIMIT_MAX = 300;
private const RATE_LIMIT_WINDOW = 60; // seconds private const RATE_LIMIT_WINDOW = 60; // seconds
private const ITEMS_PER_PAGE = 30; private const ITEMS_PER_PAGE = 30;

View File

@@ -532,6 +532,19 @@
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+\\\\\\\ to: ryqustol a5179270 "import dialog: add Terminé button, fix padding, make success permanent, avoid POST resend" (rebased revision) +\\\\\\\ to: ryqustol a5179270 "import dialog: add Terminé button, fix padding, make success permanent, avoid POST resend" (rebased revision)
++ $linkName = $link['name'] ?? ''; ++ $linkName = $link['name'] ?? '';
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: ryqustol a5179270 "import dialog: add Terminé button, fix padding, make success permanent, avoid POST resend" (rebased revision)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
- $linkName = $link['name'] ?? '';
- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: nqmqrqmo 2b244c73 "fix: obfuscate email in contact links, raise rate limits, make Libre toggleable" (rebased revision)
$linkName = $link['name'] ?? '';
$linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
$linkLockedYear = $link['locked_year'] ?? null;
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+\\\\\\\ to: nqmqrqmo dd511b0d "fix: obfuscate email in contact links, raise rate limits, make Libre toggleable" (rebased revision)
++ $linkName = $link['name'] ?? '';
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; ++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
?> ?>
<tr class="admin-table-row" onclick="event.stopPropagation(); window.open('/partage/<?= urlencode($link['slug']) ?>', '_blank')" style="cursor:pointer"> <tr class="admin-table-row" onclick="event.stopPropagation(); window.open('/partage/<?= urlencode($link['slug']) ?>', '_blank')" style="cursor:pointer">

View File

@@ -108,18 +108,17 @@
<fieldset> <fieldset>
<legend>Degré d'ouverture</legend> <legend>Degré d'ouverture</legend>
<p>Options de visibilité disponibles dans le formulaire d'ajout de TFE.</p> <p>Options de visibilité disponibles dans le formulaire d'ajout de TFE.</p>
<p class="param-note">L'option <strong>Libre</strong> ne sera activée qu'à partir de l'année académique prochaine.</p>
<form method="post" action="actions/settings.php" class="param-form"> <form method="post" action="actions/settings.php" class="param-form">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>"> <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<input type="hidden" name="section" value="formulaire"> <input type="hidden" name="section" value="formulaire">
<label class="param-checkbox"> <label class="param-checkbox">
<input type="checkbox" name="access_type_interdit_enabled" value="1" <input type="checkbox" name="access_type_libre_enabled" value="1"
<?= ($siteSettings['access_type_interdit_enabled'] ?? '1') === '1' ? 'checked' : '' ?>> <?= ($siteSettings['access_type_libre_enabled'] ?? '0') === '1' ? 'checked' : '' ?>>
<span> <span>
<strong>Interdit</strong><br> <strong>Libre</strong><br>
<small>TFE non disponible en physique ni sur le site</small> <small>Libre accès — TFE accessible publiquement sur la plateforme et en bibliothèque</small>
</span> </span>
</label> </label>
@@ -132,12 +131,12 @@
</span> </span>
</label> </label>
<label class="param-checkbox param-checkbox--disabled"> <label class="param-checkbox">
<input type="checkbox" name="access_type_libre_enabled" value="1" <input type="checkbox" name="access_type_interdit_enabled" value="1"
<?= ($siteSettings['access_type_libre_enabled'] ?? '0') === '1' ? 'checked' : '' ?>> <?= ($siteSettings['access_type_interdit_enabled'] ?? '1') === '1' ? 'checked' : '' ?>>
<span> <span>
<strong>Libre</strong><br> <strong>Interdit</strong><br>
<small>Libre accès — disponible à partir de l'année académique prochaine</small> <small>TFE non disponible en physique ni sur le site</small>
</span> </span>
</label> </label>

View File

@@ -83,7 +83,7 @@ function renderEntries(array $entries): string
fn($e) => !empty($e), fn($e) => !empty($e),
); );
foreach ($emails as $email): ?> foreach ($emails as $email): ?>
<a href="<?= EmailObfuscator::mailto($email) ?>"><?= htmlspecialchars($email) ?></a> <a href="<?= EmailObfuscator::mailto($email) ?>"><?= EmailObfuscator::email($email) ?></a>
<?php endforeach; <?php endforeach;
?> ?>
</address> </address>