The previous getSanitisedContent() method fell back to strip_tags() when
HTMLPurifier was unavailable. This fallback was insecure as strip_tags()
does not sanitise attributes, allowing XSS via onclick, onerror, and
javascript: URLs.
Changes:
- Created Services/HtmlSanitiser.php using HTMLPurifier as the sole sanitiser
- Added ezyang/htmlpurifier as a required dependency in composer.json
- Added boot-time validation that throws RuntimeException if missing
- Removed insecure strip_tags() fallback from ContentItem model
- Added 30+ unit tests covering XSS attack vectors
Closes SEC-002 from TODO.md
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Core\Mod\Tenant -> Core\Tenant
- Mod\Agentic -> Core\Mod\Agentic
Part of namespace restructure to align with L1/L2 module conventions.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Aligns content module namespace with the standard module structure
convention (Core\Mod\{Name}) for consistency across the monorepo.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>