История коммитов

simba77
refactor(consent): sanitize the title and the banner by policy
Both formatters built an HTMLPurifier config of their own with an HTML.Allowed
string; the same allowances now live in HtmlPolicy::Inline and
HtmlPolicy::InlineWithParagraphs, and the formatters only choose between them.

toPlainText of a title now sanitizes before stripping the tags, so the body of
an element that was never allowed cannot reach a breadcrumb as text; it also
collapses runs of whitespace, which a one-line title does not notice.

Adds the first tests of the consent module: which policy each formatter asks
for, and what survives it.
simba77
refactor(security): move the remaining purifier callers to the sanitizer
The models and the legacy classes that reach for the container by hand now ask
for HtmlSanitizerInterface: ForumMessage, DownloadFile, NewsArticle,
UserMutators, Comments, the news comments controller and ArticleTextRenderer.

News\Application\Utils\Helpers::purifyHtml is gone; NewsArticle sanitizes on
its own and no longer reaches from Domain into the Application layer for it.
simba77
refactor(security): take the sanitizer interface in the injected consumers
Nine services, use cases and controllers asked the container for a raw
\HTMLPurifier; they now take HtmlSanitizerInterface and speak of policies
instead of the library. Behaviour is unchanged: every call site keeps the
default rich-content policy it had.

The tests that stubbed the vendor class mock the interface instead, and
ItemContentFormatterTest asserts what the formatter owes (null for no content,
sanitized before it becomes Markup) rather than re-testing the sanitizing that
HtmlSanitizerTest already covers.
simba77
feat(security): introduce a policy-based html sanitizer
Callers state what kind of content they have (HtmlPolicy) and ask
HtmlSanitizerInterface to clean it; which library does the work is now an
implementation detail of a single class.

- HtmlPolicy: RichContent, Inline, InlineWithParagraphs
- HtmlPurifierFactory holds every policy, including the two configs that the
consent module used to build for itself
- the definition of the rich policy is cached in data/cache/htmlpurifier, so it
is no longer recompiled on every request and no longer written into vendor/
- Johncms\Security\HTMLPurifier is deprecated and delegates to the factory
- 85 tests covering xss vectors, preserved editor markup, policy boundaries and
multibyte text
simba77
ci(node): keep the emnapi peer entries in the lock file
The npm of the runner requires @emnapi/runtime in the lock file while the older npm of the development machine drops it, so npm ci failed. The lock file is regenerated with the npm version of the runner and now satisfies npm 10 and 11 alike.
simba77
ci(node): run the build on node 24
npm 11 drops the @emnapi/* peer entries of an optional package from the lock file, and npm 10 of node 22 refuses to install without them, so npm ci failed. The workflow now uses the same node as the development environment, with the setup-node action updated and the npm cache turned on.
simba77
chore: update node packages
simba77
fix(logs): write php errors to the log of the application
The boot pointed the php error_log at a second file, so failures happening before the error handlers exist ended up in errors-<date>.log instead of johncms-<date>.log. LoggerFactory now tells which file it writes to, and log_errors is on outside DEBUG as well.
simba77
chore: update composer packages
simba77
docs: update the submodule pointer