Журнал изменений

Журнал изменений

Фильтр и поиск

Всего: 1347 Доступных коммитов | Отфильтровано: 1347 Коммиты | Страница: 31 из 135
27.07.2026
Fix: whitelist inline data URIs in the renderer and refuse them in links
Автор: Eduard Laas | Дата: 12:06 27.07.2026

The parser passed any data: URI through untouched, so a guest without upload rights had an unlimited byte channel into the database and a clickable data:text/html href in every comment. Enforcement now happens at render time, which also covers rows that are already stored.

Core changes:

  1. Image sources (core/classes/parser.php):
  • Accept a data: source only as a whitelisted base64 raster image
    • Allowed types are png, jpeg, jpg, gif and webp, matched case-insensitively
    • The decoded payload must not exceed Parser::EMBEDMAX (65536 bytes)
    • Anything else returns null and renders the existing image placeholder
  • Cap the encoded length before the regex or base64_decode() runs
    • A multi-megabyte payload is refused without allocating a decoded buffer
  • Force the derived file name to 'image' for data: sources
    • A rejected payload no longer leaks into the alt and title attributes
  • Cover all three ingestion paths in both safe and unsafe mode
    • Markdown images, BB [img] and persisted raw <img src> repairs
  1. Link contexts (core/classes/parser.php):
  • Refuse data: in filterUrl() regardless of the safe flag
    • Untrusted guest content is rendered unsafe, where the policy was skipped
    • Trusted content keeps ftp:, tel: and other hrefs as before
  • Hand data: image sources straight to the whitelist
    • Safe mode previously rewrote every legitimate embed to src="#"
  1. Shared limit and meta images (plugins/editors/toastui/driver.php, core/system.php):
  • Publish Parser::EMBEDMAX to the editor as its client-side embedmax
    • The client limit is UX only and can no longer drift from the server
  • Drop data: sources in getImgText() so og:image points at a real resource

Benefits:

  • Upload permissions can no longer be bypassed through an inline embed
  • Oversized rows, heavy pages and bloated backups are prevented at the source
  • A data: URI can never appear in an href, only as escaped text

Technical notes:

  • Existing rows are covered because the rule runs at render time
  • Legitimate embeds above 64 KiB stop rendering and show the placeholder
  • ParserFixturesTest covers both modes, hostile MIME tricks, whitespace variants, the exact size limit and a multi-megabyte payload
Fix: fail-safe cache, log and counter writes with cookie-free public responses
Автор: Eduard Laas | Дата: 12:06 27.07.2026

Partial writes were reported as success, so a truncated cache body or a torn counter record could survive as valid data. Public responses could also carry a Set-Cookie header into a shared proxy, letting one visitor's state reach the next one.

Core changes:

  1. Atomic cache body write (core/classes/cache.php):
  • Treat a short write as a failure instead of trusting a non-false return
    • The temp file is removed without the error-suppression operator
    • Only a complete body ever reaches the target path through rename()
  • Drop every pending Set-Cookie when emitting Cache-Control: public
    • A shared proxy or CDN can no longer store a visitor-bound cookie
    • Sessions and CSRF are unaffected: token and captcha pages are no-store
  1. Append safety for counters and logs (core/system.php):
  • Rework addFile() append mode around fopen/flock/fwrite
    • The write position is recorded before writing
    • An incomplete append is rolled back with ftruncate()
    • A short write is reported as a failure to the caller
  1. Contract test coverage (tests/Support/contract_probe.php, tests/Unit):
  • Extend the CLI probe with a scratch COUNTER_DIR and new scenarios
    • stathit drives the real updateStatsTrack() per process
    • appendfail proves a blocked append is reported, not silently lost
    • geoip measures the streaming reader against the real corpus
  • Add StatsContractTest and GeoipReaderTest, extend PageCacheContractTest
  • Add the getvar scenario used by the input contract tests that follow

Benefits:

  • A truncated write can no longer be served as a valid cached page
  • Statistics stay exact under parallel hits instead of losing records
  • Public caching becomes safe for shared proxies

Technical notes:

  • No storage format changes; counters and cache files keep their layout
  • Backward compatible: only failure paths and response headers changed
  • Documentation updated in docs/PERFORMANCE.md; the remediation plan is done and its document removed
24.07.2026
Style: shorten new statistics and cache-contract comments to line limit
Автор: Eduard Laas | Дата: 12:55 24.07.2026

Trims the function comments added by the performance remediation work to the 180-character line limit while keeping their meaning; no code or behavior changes.

Core changes:

  1. Comment wording (core/system.php, core/classes/cache.php):
  • updateStatsCookie, updateStatsTrack, marker contract, and page-cache contract comments rewritten concisely within one line each
  1. Test probe (tests/Support/contract_probe.php):
  • header comment shortened to the same limit

Technical notes:

  • legacy over-length lines elsewhere are untouched, out of scope
Fix: bounded page-cache identity and fail-safe exact statistics
Автор: Eduard Laas | Дата: 12:46 24.07.2026

Closes the correctness and availability gaps from the 2026 performance remediation plan: the public page cache can no longer be amplified by arbitrary query input or foreign hosts, and the exact visitor statistics survive IO failures without losing or double-counting entities.

Core changes:

  1. Bounded page-cache identity (core/system.php, core/classes/cache.php):
  • Cache::getQueryVars() validates the request query against a per-route key/value regex contract, dropping tracking keys centrally
    • unknown, duplicate, or malformed keys render live and create no entry
    • replaces the now-dead Cache::filterCacheUrl()
  • getCacheRouteVars() adds the canonical homeurl host gate and feeds getPageHash(), which builds the pc2-versioned identity from validated values so num=1, encodings, and foreign Host values cannot fork entries
  1. Dynamic-region marker contract (core/system.php):
  • checkDynamicMark() allowlists token scopes ajax/account/scheduler, captcha action login, and positive voting IDs
  • invalid emitters poison the build, log, and fall back to live rendering
  • the contract is revalidated at serve time so forged markers stay inert
  1. Fail-safe exact statistics (core/system.php):
  • v2 stats cookie carries only session metrics and country cache; no client state can suppress an exact count
  • ips.log/user.log sets are the source of truth with hosts/users counters derived from set size, so any single write failure self-heals
  • statistic.log is replaced atomically via temp file and rename under a stable statistic.lock; partial writes can never trigger a day reset
  • rollover verifies full day lines, rolls back short appends, aborts on any archive/rename/unlink failure before destroying state, and names archives from the data date
  • checkUniqueIp()/check_user() consolidated into updateStatsTrack()
  1. Regression coverage (tests/):
  • PageCacheContractTest exercises the production functions directly and through contract_probe.php, which boots the real core per scenario with LOGS_DIR redirected to scratch so failure paths stay assertable

Benefits:

  • guest requests cannot grow the cache key space or host namespaces
  • exact unique hosts/users stay correct across injected IO failures
  • rotation failures preserve data and retry instead of deleting sources

Technical notes:

  • pc2 identity version makes pre-contract cache files unreachable; normal GC removes them without a purge
  • v1 stats cookies are discarded by design (disposable analytics state)
  • OPcache/cron deployment (Batch 4) remains a manual environment step; docs/PERFORMANCE-REMEDIATION-2026.md stays OPEN with recorded results
23.07.2026
Perf: core hot-path overhaul with safe guest page cache and dynamic regions
Автор: Eduard Laas | Дата: 18:56 23.07.2026

Implements the full 2026-07 performance plan: exact locked statistics, deferred post-response tracking, derived config, streaming GeoIP, shared category map, parser cache, cache GC, and a guest page cache that keeps every visitor-bound token isolated through signed dynamic-region markers.

Core changes:

  1. Statistics and tracking (core/system.php):
  • Rewrite updateStatsTrack around one c+ handle with a single LOCK_EX
    • read, day/month rotation, truncate and write share one lock
    • first hit of a new day now records its own IP (hosts=1) and user
    • three duplicated counter-field blocks collapsed into one
  • Replace sessions.log with a signed base64url stats cookie
    • fields v1|sid|fst|lst|hits|country|cts|uniq-day|ip-hash, HMAC via getSecret('stats'), verified with hash_equals, tamper resets session
    • country carries a 24h TTL and is bound to the IP hash
    • updateSessionState removed, sessions.log and its global lock retired
  • Split tracking into pre-output cookies and post-response writes
    • addDeferredTask/setDeferredTasks queue with shutdown backstop
    • session_write_close before deferred writes, drains on all exits
    • news article counter update moved into the deferred queue
  • Literal stripos matching for bots, fbots, and auto_links patterns
    • removes regex injection from config and DB values, caches results
  • Conditional lastvis update (60s window) and _session upsert
  1. Session schema (setup/sql/table.sql, table_update6_3.sql):
  • UNIQUE KEY on _session.uname for new installs
  • mksessuniq migration: dedup by max(time, id), drop old index, add unique, idempotent on re-run; code uses INSERT ... ON DUPLICATE KEY
  1. Derived config cache v2 (core/system.php):
  • getConfig stores derived data in config/local.php (version 2)
    • per-theme asset manifests with stat fingerprints, parsed SEO graph/schema templates, logo dimensions per theme
  • doCss/doScript drop per-request glob/stat sweeps, bundle hash only computed when bundling is enabled
  1. Streaming GeoIP (core/classes/geoip.php):
  • MMDB reader works through fseek/fread ranges instead of loading the whole database file; metadata resolved from the last 128 KiB
    • byte-exact with the old reader, peak memory 25 MB -> 2 MB
    • fread(handle, 0) guarded (old substr was silently empty)
  1. Category map and parser cache (core/system.php, core/helpers.php, modules/news/index.php):
  • getCategoryMap consolidates two duplicated category queries behind an epoch-keyed data cache with raw titles
  • news list caches filterContent output keyed by content hash, parser config, theme, locale, and parser version; [block]/[hide]/[usephp]/ [attach] and local <img> content bypasses the cache
  1. Guest page cache with dynamic regions (core/system.php, core/classes/cache.php, core/security.php, blocks/*):
  • Signed markers [[sldyn:type:par:hmac]] via getSecret('dynreg') keep user content from forging substitutable markers
  • Regions: CSRF tokens (getPageToken), captcha (getPageCaptcha), and the whole voting widget; cache files contain zero live tokens
  • Poison guard: a live getSiteToken/getCaptcha call during a cacheable build prevents storing the page entirely
  • Default-deny route allowlist inside checkPageCache (news list only)
  • Fail-closed sidecar with body hash and dyn flag; dynamic pages are no-store and never answer 304; substitution runs on hit and miss
  • cache = 1 enabled in config/global.php
  1. Cache GC (core/classes/cache.php, core/system.php):
  • deleteStaleTree recursively sweeps storage/cache/templates and the data cache joins the cachegc scheduler job

Benefits:

  • Guest cache hit generation time 0.307s -> ~0.055s (5.3x)
  • Uncached pages roughly halved (search 0.17s -> 0.09s)
  • Day counters survive concurrent requests and day rollover
  • CSRF/captcha isolation between visitors verified with two cookie jars

Technical notes:

  • config/local.php cache version bumped to 2, rebuilt automatically
  • theme asset changes now require a config rebuild (admin save or deleting config/local.php)
  • _session migration applied to the dev DB; verify on MySQL before production rollout (developed against MariaDB 11.7)
  • ip/agent freshness for logged-in users delayed up to 60 seconds
  • docs/PERFORMANCE.md updated; docs/PERFORMANCE-REMEDIATION-2026.md is the completed plan, ready for deletion after review
Fix: Harden OAuth account lifecycle and transaction handling
Автор: Eduard Laas | Дата: 13:41 23.07.2026

Strengthen OAuth account creation, linking, unlinking, and login finalization with fail-closed transactions, atomic temporary-state consumption, safer provider validation, and consolidated session handling. Add focused regression coverage for JWT validation, transaction failures, redirect safety, and unlink invariants.

Core changes:

  1. OAuth security and lifecycle (oauth.php, modules/account/index.php):
  • Harden redirect, provider response, JWKS, claim, and one-time state handling
    • Bound provider response bodies and validate JSON content types
    • Consume callback state atomically and preserve retryable pending records
  • Make account creation and unlink operations transaction-safe
    • Roll back failed user/link creation as one unit
    • Serialize unlink operations and protect the final login method
  • Consolidate normal and OAuth login session finalization
  1. Database transaction support (pdo.php):
  • Add begin, commit, and rollback primitives for shared PDO connections
  • Normalize transaction exceptions to the existing boolean error contract
  1. Admin output and regression tests (modules/account/admin/index.php, tests/Unit):
  • Render provider identity data through escaped text template fields
  • Cover redirects, claims, JWT signatures, transaction failures, and unlink outcomes

Benefits:

  • Prevents orphan accounts, replayed callbacks, unsafe redirects, and last-method unlink races
  • Reduces duplicate login state handling and unnecessary unlink queries
  • Improves maintainability with isolated OAuth and database regression tests

Technical notes:

  • Uses the existing OAuth tables and configuration without schema changes
  • Preserves current routes and backward-compatible account behavior
Feature: OAuth2/OIDC login (Google/Microsoft) replaces uLogin, template consolidation
Автор: Eduard Laas | Дата: 10:44 23.07.2026

Implements a dependency-free OAuth2 Authorization Code Flow with PKCE as the new external sign-in, removes the legacy uLogin integration completely and consolidates theme templates while keeping both themes self-contained.

Core changes:

  1. OAuth client (core/classes/oauth.php, config/oauth.php):
  • Static Oauth class: provider config, PKCE auth URL, token exchange, hardened curl layer, JWKS cache with rotation, full RS256 id_token validation (exp/nbf/iat with skew, iss list, aud/azp, nonce)
    • Microsoft email trusted only with xms_edov or consumer tenant
    • state bound to the browser via __Host-oauth-st cookie (anti link-CSRF)
  • One-time flow storage in {prefix}_oauth_temp (state/pending kinds), permanent links in {prefix}_user_oauth, audit log in log_oauth.log
  1. Account module (modules/account/index.php):
  • New ops: oauth_init, oauth (callback), oauth_finish, oauth_unlink
    • first sign-in offers linking to an existing account or creating a new one; OAuth-created accounts store an unguessable '!'-prefixed marker
    • unlink refuses to remove the last login method
  • uLogin op network and all its guards removed; login/passmail/savepass work by password state instead of the dropped users.network column
  • users_temp now stores bcrypt hashes; activate() keeps a transitional fallback for old plaintext rows
  1. Admin (modules/account/admin/index.php):
  • Provider settings (activation, Client ID/Secret) in account preferences
  • oauthlist view with forced unlink via POST buttons, link cleanup on user delete, pending-users list shows the activation code instead of the stored password
  1. Schema and migration (setup/sql/table.sql, table_update6_3.sql):
  • New tables user_oauth and oauth_temp with engine/charset placeholders
  • Idempotent migration archives legacy identities as provider 'ulogin' rows and drops the users.network column
  1. Themes (templates/admin, templates/lite):
  • OAuth buttons, finish form and profile links tab; unified post-button
  • Both login blocks render one block-login-form fragment via include, fixing the broken sidebar login (wrong field name, missing CSRF token)
  • block-search-form merged into search-form; nine twin fragments synced between themes; speed dial fan opens only from its toggle
  • Session write lock released before scheduler jobs (index.php) so long pseudo-cron runs no longer block the visitor session

Benefits:

  • Passwordless sign-in without composer dependencies on shared hosting
  • No plaintext passwords at rest anywhere in the system
  • Fewer duplicated templates with identical twin fragments across themes

Technical notes:

  • Redirect URI: index.php?name=account&op=oauth (HTTPS required)
  • Providers ship disabled; admin help documents the full setup procedure
  • Verified by 29 JWT unit tests, mock-OIDC E2E flows and phpunit (194 OK)
22.07.2026
Feature: cabinet home as Command Deck with progress ring and live panels
Автор: Eduard Laas | Дата: 06:25 22.07.2026

Port the account cabinet (index.php?name=account) to the Command Deck layout from the 2026 demos: identity rail, action tile shelf, live scroll panels and a full-width lower section, all built on existing profile and category mechanics instead of new one-off styles.

Core changes:

  1. Cabinet page and data (modules/account/index.php, core/user.php):
  • profil() renders the new account-home partial
    • identity rail: profile avatar ring, online status, rating chip and the community progress ring (points, reached group, level percent)
    • panels: last incoming private messages, favorites, last activity feed and the RSS reader with the legacy preset select restored
  • getUserNavItems() builds icon/tone tiles per active module with the new-PM badge and favorites counter; getUserNav() renders the compact strip for inner cabinet pages via the account-nav fragment
  • getUserLevelData() extracts the ring/level computation shared with the public profile view; rssfeed() serves the feed over htmx POST
  • getFavoriteList() initializes its arrays (warnings on empty lists)
  1. Lite templates and styles (partials/account-home.html, fragments/account-nav.html, assets/css/theme.css):
  • panels reuse sl-profile-panel, sl-profile-scroll and the single sl-profile-feed-live spinner, now title-line aligned in profile and cabinet; panel headings are flex with the spinner in the right corner
  • action tiles reuse the files-category icon mechanics (sl-cat-ico with sl-cat-tone-N cycling); favorites rows carry configured module icons
  • sl-cab-* additions cover only the deck grid, tiles, rows, badge, progress ring (sized and shadowed like the avatar) and the RSS forms
  • legacy PNG navigation removed (images/account/*.png, catflex flags)
  1. RSS reader (core/system.php):
  • rss_read() decodes the entity-encoded config template, so stored markup renders instead of printing as text

Benefits:

  • cabinet matches the profile design language with no duplicated styles
  • module tiles reflow automatically when modules are disabled
  • RSS reading works inline over htmx without page reloads

Technical notes:

  • RSS requests must POST the url (security url_get blocks URLs in GET)
  • no schema changes; legacy account nav markup and icons are gone
21.07.2026
Feature: profile status and ring polish, activity indicator, cabinet demos
Автор: Eduard Laas | Дата: 15:33 21.07.2026

Post-release polish of the redesigned profile plus design demos for the account home page.

Core changes:

  1. Profile status and identity (modules/account/index.php, account-profile.html):
  • Online/offline chip with bi-wifi / bi-wifi-off on success/danger tokens; new global _OFFLINE constant and capitalized _ONLINE in all six locales
  • Avatar ring colored by group like the user block: special group color first, otherwise the highest reached points group, validated hex passed as --sl-profile-ring
  • Signature block, site and hub queries reuse the existing selects, no extra queries added
  1. Activity feed (core/user.php, account-profile-feed*.html, theme.css):
  • Entry dates render through the standard date-badge fragment at system chip sizes; custom 10px sizing removed
  • Spinning bi-arrow-repeat indicator on the tabs row in primary tone; overlay positioning keeps tab panels at full feed width
  • Indicator pauses together with the hovered feed scroll via :has()
  1. Avatar geometry (theme.css):
  • box-sizing fixes for the profile avatar and the user block ring and avatar, so images no longer overlap the conic ring and sit centered
  1. Dead code (theme.css, modules/account/index.php):
  • unused sl-user-group-special styles and the unused g.color column in the profile select removed
  1. Cabinet design demos (demo/):
  • account-home-2026.html with six directions and account-home-final-2026.html with three finals, a module toggle simulator, auto-fit action shelf, files-category tone tiles and auto-scrolling panels; deferred work listed in docs/PROFILE-BACKLOG.md

Benefits:

  • Status and group identity readable at a glance on any profile
  • Feed visuals match the sitewide chip and date language
  • Ready-to-transfer blueprint for the account home redesign
17.07.2026
Feature: redesign the public user profile as a dense split card
Автор: Eduard Laas | Дата: 12:49 17.07.2026

Replace the legacy table profile (account&op=view) with the approved Dense Split composition: identity rail, trust strip, symmetric fact panels, module contribution hub and a tabbed last-activity feed, all rendered from live data in four SQL queries.

Core changes:

  1. Profile view (modules/account/index.php):
  • Rebuild view() around template data arrays: role-aware fact panels, trust strip, level progress and group chips
    • online status via a subquery in the main user select
    • one groups select drives chips, rank fallback and level thresholds
    • module hub counts, weighted ratings and favorites in one UNION ALL
    • hidden fields keep the localized placeholder for panel symmetry
  • profil() reuses the shared feed; the legacy last() helper is removed
  1. Shared profile helpers (core/user.php):
  • getProfileModules() maps module tables, icons, rating column pairs and favorites keys in one place
  • getProfileLastView() renders the per-module activity feed from a single UNION ALL round-trip and respects per-module rating settings
    • computed union columns are aliased because a literal 0 column would otherwise shadow numeric row index 0 under FETCH_BOTH
  1. Theme (templates/lite):
  • account-profile.html rewritten for the split card; new feed partials account-profile-feed.html and account-profile-feed-list.html
  • sl-profile-* styles on base.css tokens; chip labels truncate with an ellipsis; dead sl-table-acc-info and sl-user-group-special styles removed
  • signature renders as the system blockquote and is visible to everyone
  1. Feed auto-scroll (plugins/system/slaed.js):
  • setProfileScrolls clones visible feed entries and animates them with reduced-motion and hover-pause support, re-measuring on tab switches
  1. Language constants (lang/, modules/account/lang/):
  • nine ACCOUNT* module constants and the global _LASTACTIVITY in all six locales

Benefits:

  • Profile page cost drops from ~17 module queries to 4
  • One module map feeds the hub, the feed and future consolidation
  • Deferred follow-ups documented in docs/PROFILE-BACKLOG.md
Хотите опробовать SLAED CMS в действии?
Идеи и предложения
Обратная связь
Подтверждение

Поделиться
QR-код

Предварительный просмотр