Repository navigation
v1.10.0
[1.10.0] - 2026-10-31
Added
- Plugin integrity manifests: Every plugin installed through the admin panel now records a SHA-256 digest over
plugin.iniplus all PHP files in its directory (tbl_plugin.plugin_manifest); activation refuses files that no longer match - Plugin manifest migration: New CLI-only
install/migrate-plugin-manifest.phpadds the integrity column idempotently and supports an explicit--backfillfor existing installations - Plugin schema SQL allowlist: New
validate_plugin_schema_sql()permits onlyCREATE TABLE,CREATE INDEX,ALTER TABLE ... ADD, andINSERTstatements against the plugin's own tables, rejecting multi-statements, comments, and destructive DDL/DML - Plugin audit logging: New
plugin_audit_log()writes structured JSONL entries for install, activation, manifest rejection, and schema rejection events - Environment detection utility: New
scriptlog_app_is_development()resolvesAPP_DEVELOPMENT,APP_DEBUG, andAPP_ENVwith a safe non-development default, andscriptlog_scrub_server_secrets()removes credentials from$_SERVER - Administrator-only plugin mutations: New
PLUGIN_MANAGEaction constant gates install, activate, deactivate, and delete; plugin listing remains available to managers - Protected-post derived session keys: New
decrypt_post_with_key()decrypts using a keyed digest instead of storing the plaintext password in the session - Rate-limit and unlock-log hardening: The rate-limit cache and failed-unlock log directories now receive a deny-all
.htaccessfile - Prism.js browser coverage: Ten Playwright specs plus shared fixtures cover highlighting, inline code, plugins, RTL, CSP, console errors, double initialization, and accessibility
- Playwright Test Agents guide: New
docs/dev-docs/PLAYWRIGHT_AGENTS_GUIDE.mddocuments the planner, generator, and healer workflow - Troubleshooting coverage: Expanded guide entries for bootstrap guard failures, admin UI, and plugin integrity issues
Changed
- Theme upload flow:
ThemeControllernow separates form rendering, upload extraction, validation, and installation into focused helpers - Plugin upload flow: The plugin directory is derived from the archive name and must match
plugin.ini; the manifest digest is computed before the row is written, and invalid uploads render the form with errors instead of redirecting silently - Search combobox accessibility: The suggestion list now uses
aria-activedescendantwhile DOM focus stays on the input, and the active option is scrolled into view inside the popup - Pagination markup: The shared paginator partial wraps the existing pagination
<ul>in a labelled<nav>instead of nesting a second<ul> - Prism.js focus indicator: The copy button restores a visible
:focus-visibleoutline that the theme reset previously removed - Controller and service finality: Controllers and services are marked
finalwhere they are not intended for extension - Scheduled post throttle signature:
publishDuePostsThrottled()now declaresstring/intparameters and anintreturn type - PHP runtime guidance: README and Developer Guide recommend PHP 8.3+ while documenting the retained PHP 7.4 compatibility floor
- Documentation: Database Schema, Plugin Developer, Theme Developer, Testing, and Troubleshooting guides updated for manifests, upload rules, SRI hashes, and test counts
Fixed
- Rate limiter failure handling: An unwritable counter file now fails open with a log entry instead of raising a warning, and the default cache path no longer produces a doubled directory separator
- Spurious 301 responses: The shutdown handler promotes only genuine fatal errors, so suppressed warnings and notices can no longer turn a successful request into a redirect
- API session bootstrap:
api/index.phpno longer re-registers the session save handler while a session is active, fixing 301 loops that surfaced as generic search errors - Protected post legacy sessions: Existing plaintext-password sessions still unlock once and are migrated to the derived key
- Language switch type safety:
?switch-lang[]=xand other non-scalar values are ignored instead of raising aTypeErrorin the debug handler - Privacy policy CSRF ordering: The form token is generated only after POST validation, so submissions no longer fail intermittently
- Privacy deletion confirmation: Data deletion requests now require the confirmation checkbox before submission
- Installer table detection:
check_table()now requires the full expected table set and includes API keys, downloads, themes licenses, consents, data requests, privacy, language, and translation tables - Whoops secret exposure: Debug pages blacklist database, mail, key, and session credentials and no longer assume
app_url()exists - Plugin upload cleanup: Invalid or mismatched plugin archives are deleted and reported instead of leaving partial directories behind
Security
- Fail-closed plugin activation: A missing, malformed, or mismatched manifest blocks activation before any plugin code is loaded and writes an audit entry
- Plugin schema sandboxing: Plugin
schema.sqlruns only through an allowlist that binds statements to the plugin's own tables - Credential scrubbing: Dotenv-loaded secrets are removed from
$_SERVERduring bootstrap without altering$_ENV, and Whoops blacklists common secret keys - Protected-post session hardening: Session state stores a keyed digest rather than the plaintext post password, with transparent migration for legacy sessions
- Plugin management authorization: Install, activation, deactivation, and deletion are restricted to administrators
- Upload validation: Plugins must match their declared directory and carry a valid integrity manifest; theme uploads retain per-form CSRF validation
- Cache and log exposure: Rate-limit counters and failed-unlock logs are protected by deny-all
.htaccessfiles - Request input hardening: The language switcher accepts only scalar two-letter locale codes
Deprecated
- None
Removed
- None
Notes
Minor release focused on plugin supply-chain integrity, defense-in-depth for protected content, and request/bootstrap reliability. No public API, database schema, or configuration breaking changes. Existing installations should run php install/migrate-plugin-manifest.php and re-install or deliberately backfill plugins before activation. Test suite: 1,255 unit tests and 3,075 assertions passing (10 skipped); PHPStan clean.
Codename
Maleo Senkawor (retained) – Honoring Macrocephalon maleo, the critically endangered megapode endemic to Sulawesi, Indonesia.
Comparison
- Previous release: v1.9.1
- Changes since v1.9.1: 84 modified files and 18 new files