You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Security
IP geolocation debug logging — hardened to never store a raw client IP, even with debug_geofence enabled. A security audit found IpGeolocation::get_location() wrote the raw IP into the ActivityLog LEVEL_DEBUG trail on the cache-hit and service-success paths (and via the returned location.ip field echoed into the log), inconsistently with the already-hashed fetch_from_service path and the salted-hash treatment in PreflightTelemetry. All debug-log call sites now emit a truncated SHA-256 (hash_ip()) and redact the ip field of any logged location array via a new redact_location_ip() helper; the value returned to callers and the cache entry are unchanged (the contract location['ip'] is preserved). Low severity — the path is gated behind the off-by-default debug toggle and an IP is low-sensitivity operational data — but it removes a residual LGPD/GDPR exposure and aligns the module with the plugin's IP-hashing convention. No behavior change for callers. (#596)
Changed
Internal refactor (#563 — modular-monolith hardening) — decomposed the largest god-classes and the shared static hubs, behavior-preserving throughout (characterization tests first; markup/queries/hooks unchanged): the FormProcessor submission flow → a pipeline of testable guard/stage classes (~1047→85 lines); CapabilityManager → CapabilityMigrator / RoleRegistrar + a slim runtime manager; the Core\Utils god-utility dismantled into focused homes (FilenameHelper, RequestInput, AssetHelper, HtmlPolicy, Capabilities, SuccessHtmlRenderer, …), leaving only generic primitives; RateLimitChecker → per-dimension strategy classes; RecruitmentCsvImporter → CsvParser / CsvValidator / CsvStagingService / CandidatePersister (~1554→310); the monolithic Activator → per-module activators; the five large repositories → *Reader / *Writer behind a delegating façade; admin-renderer view/logic separation; and typed per-option-group settings readers GeolocationSettingsReader / RateLimitSettingsReader. Full suite green; PHPStan L8 + WPCS clean. (Detail in the linked PRs.)
Internal refactor (#589) — retired the UserManager→CapabilityManager and ActivityLog→ActivityLogQuery back-compat delegations (all call sites repointed); split RecruitmentCandidateRepository into reader/writer; decomposed the public-CSV classes and the ReregistrationAdmin / RecruitmentAdminPage god-classes into controller + renderer; extracted the large inline admin markup into templates/ partials; and split PdfGenerator into renderer + data-assembly. Behavior-preserving — byte-identical markup, hooks unchanged.
Internal refactor (#591) — split the remaining admin/handler god-classes behind same-signature delegators (FormEditorSaveHandler, AudienceAdminAudience, PrivacyHandler, AdminAssetsManager, and the critical-path SubmissionHandler → SubmissionLifecycleService); read/write-split the smaller repositories (recruitment notice/adjutancy/reason/call, url-shortener, reregistration-submission); extracted SettingsActionHandler from Settings; removed the lone dead DateFormatter::flush_cache() shim and dead back-compat entry points. The remaining migration-tail shims (Encryption plaintext fallback, cpf_rf_encrypted, ficha generation_date) stay — evidence-gated, not dead code. Behavior-preserving.
Internal architecture (#563 — module boundaries) — added a dependency-free module-boundary guard (tests/Unit/ModuleBoundaryTest.php + committed baseline, 130 edges) that fails CI on any new cross-module coupling and can only shrink — regenerate with FFC_UPDATE_BOUNDARY_BASELINE=1 vendor/bin/phpunit --filter ModuleBoundary (#593). Then retired the nine static repository façades, migrating every caller to the *Reader / *Writer classes directly (row-shape types + public constants rehomed onto the readers; class_exists() availability guards and @phpstan-import-type consumers repointed). The three instance façades (AppointmentRepository, SubmissionRepository, UrlShortenerRepository) are kept by design — they are the transactional aggregate root (begin_transaction() → FOR UPDATE read → write → commit() on one $wpdb), not dead delegators. Graph unchanged at 130 edges; full suite green (5513 tests); PHPStan L8 + WPCS clean. (#594)
Internal architecture (#563 — per-module bootstrap loaders) — introduced single bootstrap entry points (AdminLoader, then ReregistrationLoader / SelfSchedulingLoader) so the orchestrator (Loader) wires each module through one symbol instead of newing up its internals inline, narrowing the Root → <module> surface. UserDashboard was deliberately left without a loader — its Root edge is capability/role lifecycle (orchestrator responsibility), not module bootstrap, so a loader would add indirection without shrinking the edge (decision documented in CLAUDE.md). Behavior-preserving; each loader pinned by a wiring test. (#600, #601)