# Maintenance and backup repairs — October 20, 2026 This follow-up addresses the three code findings in [the production review](PRODUCTION-REVIEW-2026-10-20.md). No schema migration is needed. Tests use disposable local databases, not the development or production database. Deployment and production-host acceptance work in that review still applies. ## Editor replacement - Removed the CKEditor 24 bundle and its references from all four alert/popover add/edit forms. Replaced it with locally hosted **SunEditor 3.3.3**. - Current CKEditor requires a commercial license key or GPL-compatible usage; neither was established for this project. The review allowed a maintained replacement. SunEditor uses the MIT license, included verbatim in `THIRD-PARTY-NOTICES.md`. No commercial key or runtime CDN is required. - Kept headings, bold, italic, underline, links, lists, font family/size/color, indentation, alignment, horizontal rules, tables, undo and redo. Upload and remote media tools are not enabled. - Added the shared `js/rich-text.js` adapter. Save buttons stay disabled until initialization completes. Initialization failure or timeout produces a visible message. Saving copies editor HTML into the original form field; an empty editor remains an empty optional field. - Added versioned JavaScript/CSS, archive integrity and individual asset hashes to `browser-dependencies.json`. Source checks verify the installed asset hashes and removal of the old bundle. The release builder includes the replacement, its license and dependency metadata. `Base::VERSION` changed to `1.01` to refresh cached form scripts. - Browser coverage exercises actual add/save/reopen/edit flows for both forms, including legacy table markup, fonts, lists, links and text formatting; formatted paste; filtering scripts, event attributes and unsafe links; failed asset loading; and the existing content security policy. - SunEditor 3 targets modern browsers (Chrome/Edge 119+, Firefox 125+, Safari 17.2+). Unusual legacy HTML may be normalized by its content model; arbitrary HTML is not promised to round-trip byte for byte. Existing server output protections remain necessary; client filtering is not a server trust boundary. Upstream sources: [SunEditor release](https://github.com/JiHong88/suneditor/releases/tag/v3.3.3), [installation and license](https://github.com/JiHong88/suneditor), [CKEditor licensing](https://ckeditor.com/docs/ckeditor5/latest/getting-started/installation/self-hosted/quick-start.html). ## Server-side maintenance validation - Added `classes/MaintenanceValidation.php`, shared by create and edit handlers in `Alerts.php` and `Popovers.php`. - Alert titles and popover names/titles are required and limited to 100 Unicode characters. Optional popover placeholders allow 100 characters. Optional alert messages and popover HTML allow 1,000 characters, matching existing columns. - Rejects malformed text, null bytes, and control characters in single-line fields. Required fields reject Unicode whitespace-only values. Limits count characters rather than UTF-8 bytes. - Alert dates and times must be present and use exact `YYYY-MM-DD` and `HH:MM` formats. Rejects invalid calendar dates, impossible times, nonexistent local daylight-saving times, years outside the database range, and expiration before the start. Equal start/expiration remains allowed. - Invalid requests return HTTP 422 with a specific error before any write. Title-only alerts and popovers without optional body text remain supported. - Alert/popover lookup, update and deletion IDs now use strict integer validation. The popover uniqueness lookup still allows its create form's zero exclusion ID. - Added direct HTTP tests that verify both rejection and unchanged table fingerprints. Fixed the isolated test fixture connection to use UTF-8 so Unicode assertions read the stored text correctly. ## Backup memory usage and failure cleanup - Added `classes/BackupValidation.php`. Completion validation reads at most the final **8 KiB** of the dump instead of loading the whole file into PHP memory. - Requires the completion comment at the end, rejects missing/truncated dumps, and accepts Windows or Unix line endings. Existing process exit-status, streaming checksum, atomic completion and protected output handling remain. - Moved temporary output/credential creation into the cleanup-protected block and handles credential-file creation failure safely. - Added a 64 MiB file test under a **16 MiB PHP memory limit**, including checksum generation and measured peak-memory assertion. Additional tests cover missing, tiny and incomplete files and a completion comment before unfinished data. - Deployment tests exercise a real scratch-database dump and a failed dump process, checking nonzero failure status and removal of temporary credentials, partial output and success metadata. Release tests verify the new files. ## Verification The full regression entry point is `tests/run.py`; it now includes the new maintenance HTTP and bounded-memory backup suites. It also runs source validation, authentication, authorization, club/ledger, concurrency, browser and deployment checks. The complete run passed **374 checks plus five concurrency scenarios**: | Suite | Passed | | --- | ---: | | Authentication service/templates | 49 | | Maintenance unit checks | 52 | | Authentication HTTP | 27 | | Project HTTP | 99 | | New maintenance HTTP | 47 | | Production security and ledger | 42 | | Restricted database privileges | 5 | | Browser flows | 28 | | Backup validation | 9 | | Deployment tools/release packaging | 16 | Source validation also passed for 44 PHP files, 56 JavaScript files and 39 XSL stylesheets, including jQuery and SunEditor integrity checks. The isolated loopback database server was stopped after the disposable databases were removed. No development database records, real passwords, production services or email recipients were changed by this work.