fix: suppress verbose PHP errors and bound login/audit housekeeping cost #41

Merged
GeoffMoore1979 merged 1 commits from feature/media-users-pages into master 2026-10-02 12:28:28 +02:00
3 changed files with 22 additions and 1 deletions
Showing only changes of commit 43200d7dfe - Show all commits
+6
View File
@@ -1,6 +1,12 @@
<?php
/** Shared entry-point setup: loads configuration into $config and registers the NeoCMS class autoloader. */
// Keep stack traces and filesystem paths out of responses; every engine warning still reaches PHP's own error log.
error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('display_startup_errors', '0');
ini_set('log_errors', '1');
require_once __DIR__ . '/config.php';
// URL prefix of the site root: "/neocms" for http://localhost/neocms/, "" for a site at the domain root.
+3 -1
View File
@@ -42,7 +42,9 @@ class Logger
}
@chmod($this->logsDir, 0700);
if ($this->auditEnabled) {
// Pruning scans every rotated log file; a Logger is built on every request, so do this
// occasionally rather than on each one (the same probabilistic approach as PHP's own session GC).
if ($this->auditEnabled && random_int(1, 100) === 1) {
$this->pruneExpiredLogs();
}
}
+13
View File
@@ -16,6 +16,9 @@ final class LoginRateLimiter
private int $maxIdentityAttempts;
private int $maxAddressAttempts;
/** Hard ceiling on tracked buckets, so a distributed flood of distinct addresses or login names cannot grow the state file without bound. */
private const MAX_TRACKED_KEYS = 20000;
/** Configure the protected state file and throttling thresholds. */
public function __construct(string $dataDirectory, array $options = [])
{
@@ -93,6 +96,16 @@ final class LoginRateLimiter
}
}
unset($record);
if (count($state) > self::MAX_TRACKED_KEYS) {
// Evict the least recently active buckets first; an evicted bucket simply restarts its count.
uasort($state, static function (array $a, array $b): int {
$lastA = max((int) ($a['locked_until'] ?? 0), $a['attempts'] ? (int) end($a['attempts']) : 0);
$lastB = max((int) ($b['locked_until'] ?? 0), $b['attempts'] ? (int) end($b['attempts']) : 0);
return $lastA <=> $lastB;
});
$state = array_slice($state, -self::MAX_TRACKED_KEYS, null, true);
}
}
/** Serialise state updates beneath an exclusive lock to avoid concurrent lost updates. */