fix(review) : resout findings 3e passe review (HIGH frontend + MEDIUMs backend/frontend/E2E)

Backend :
- AuditLogWriter::stripSensitive rendu reellement recursif (matche doc).
- Tests GET /api/permissions/{id} non-admin pour chaque branche OR (gap Codex).
- Gardes non-regression UserRbacProcessor : PATCH /rbac sans clef sites ne
  doit ni auto-selectionner currentSite ni exiger sites.manage.

Frontend :
- useAuditLog : renomme export trompeur fetchLogs -> fetchLogsCached, le
  nom reflete desormais le comportement (cache pollue sinon).
- RoleDrawer / UserRbacDrawer : catch explicite + message d'erreur +
  bouton save disabled si le chargement des referentiels a echoue (evite
  un ecrasement silencieux des droits).
- AuditTimeline / AuditLogDetail : `oui`/`non` passent par common.yes/no.
- AuditTimeline : Intl.RelativeTimeFormat et toLocaleString suivent la
  locale i18n courante (plus de hardcode 'fr').

E2E :
- sidebar-visibility.spec : remplace waitForLoadState('networkidle')
  fragile par attente semantique sur accountDashboardLink (stable en CI).

Tests : 237/237 green, eslint clean, php-cs-fixer clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-04-23 10:31:03 +02:00
parent 25cd6a1ecc
commit b1255bb57a
12 changed files with 271 additions and 48 deletions

View File

@@ -111,6 +111,33 @@ final class AuditLogWriterTest extends TestCase
$this->assertSame('bob@example.com', $changes['email']);
}
public function testStripsSensitiveKeysRecursively(): void
{
// Defense-in-depth : un appelant direct peut passer un payload
// imbrique (ex: relations embarquees). Les cles sensibles doivent
// etre supprimees a tous les niveaux de profondeur.
$security = $this->buildSecurityWithUser('alice');
$writer = new AuditLogWriter($this->connection, $security, $this->requestStack, $this->requestIdProvider);
$writer->log('core.User', '1', 'create', [
'username' => 'bob',
'profile' => [
'email' => 'bob@example.com',
'password' => 'leaked_in_nested',
'nested' => [
'token' => 'should_be_stripped',
'harmless' => 'kept',
],
],
]);
$changes = $this->capturedInsert[1]['changes'];
$this->assertArrayNotHasKey('password', $changes['profile']);
$this->assertArrayNotHasKey('token', $changes['profile']['nested']);
$this->assertSame('kept', $changes['profile']['nested']['harmless']);
$this->assertSame('bob@example.com', $changes['profile']['email']);
}
public function testCapturesIpAddressWhenRequestPresent(): void
{
$request = Request::create('/api/users', 'POST');