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

Официальный сайт SLAED CMS

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

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

Всего: 1051 Доступных коммитов | Отфильтровано: 1051 Коммиты | Страница: 1 / 106
Сегодня (27.07.2026)
Docs: allow single-letter counters and refresh the test inventory
Автор: Eduard Laas | Дата: 12:08 27.07.2026

The variable naming rule demanded at least two characters, which the code base contradicted in about eighty places, including the filter table in getVar(). A rule that is never followed stops guiding anything, so the exception is written down instead.

Core changes:

  1. Naming rule (CONTRIBUTING.md):
  2. Allow a single letter where the name carries no domain meaning

    • Loop counters and parameters of single-expression closures
    • Any variable holding a domain value keeps the 2-8 character rule
  3. Test inventory (docs/TESTS.md):
  4. List the unit tests that were added since the file was last updated
  5. Note that the contract tests drive production code through the CLI probe
  6. Completed plans (docs/FRONTEND-DIAL.md, docs/PROFILE-BACKLOG.md):
  7. Remove the plan documents whose work is finished

Benefits:

  • The written rules match the code that reviewers actually read
  • Newcomers find the current test layout instead of an outdated list

Technical notes:

  • Documentation only, no runtime change
Fix: admin forms read their array fields through the correct getVar keys
Автор: Eduard Laas | Дата: 12:08 27.07.2026

Five admin handlers addressed a name[] form field with a scalar key or passed an array default into a scalar filter. The result ranged from a log warning to settings that were silently discarded on every save.

Core changes:

  1. Language editor (admin/modules/lang.php):
  2. Read constants and translations by index through nested keys

    • Form fields carry an explicit index and a hidden row count
    • The saved file no longer depends on browser field ordering
  3. Stop overwriting the posted translations with the file contents

    • The loop variable holding the file body was shadowing the input
  4. Add getLangConstants() and use it in both the editor and the save handler

    • It understands single and double quoted values, so an apostrophe in a
translation survives instead of truncating the parsed value
  • Escaping on write covers the backslash as well, so a trailing backslash
can no longer break the generated file
  • Remove dead merge and unset code left from an earlier revision
  • RSS feed list (modules/rss/admin/index.php):
  • Read the 50 rows as untyped arrays so their indexes stay aligned

    • The previous scalar key produced false and wiped every feed to zeros
  • Strip the pipe separator and line breaks from the stored values
  • Drop the required attribute from the hidden feed rows

    • With 49 empty required rows the browser refused to submit the form at all
  • Comments, sitemap and account (admin/modules/, modules//admin):
  • Address the checkbox and select arrays as id[], mod[] and warn[]

    • Single-row approve and delete no longer log an array conversion
    • The sitemap module selection is stored instead of falling back to zero
    • User warnings survive a redisplay of the account form after an error

Benefits:

  • Language and feed editing actually persist what the admin entered
  • Admin actions stop writing warnings into the PHP log
  • Existing values keep their meaning: nothing is rewritten on read

Technical notes:

  • No schema or config format changes
  • The language editor normalises quoting on first save; values stay identical
  • Verified live in the admin UI with backups restored afterwards
Feature: nested form keys in getVar() with a contract test suite
Автор: Eduard Laas | Дата: 12:08 27.07.2026

Form fields like lng[ru][] could not be read at all: the key syntax stopped at one level, and a mismatched key silently produced false from filter_input(), which then reached the scalar filters. Several admin forms were broken by exactly that, so the helper is extended instead of worked around per module.

Core changes:

  1. Key syntax (core/security.php):
  2. Parse a key as a path so leading segments walk nested form fields

    • lng[ru][] returns the whole branch, lng[ru][_A] one element
    • deep[a][b][c] and row[2][id] work the same way
    • The existing forms key, key[] and key[n] behave exactly as before
  3. Walk the path once before the branches instead of per branch
  4. Missing and mismatched values (core/security.php):
  5. Treat an array reached through a scalar key as missing

    • A wrong form name now yields the default instead of false
  6. Return an array default before any scalar filter runs

    • filterNum() no longer receives an array and logs a conversion warning
  7. Contract tests (tests/Unit/InputVarContractTest.php):
  8. Exercise the real helper through the CLI probe, not a replica

    • Typed array keys still drop empty values and reindex
    • Untyped array keys return the payload untouched for index alignment
    • Nested paths are covered for post, get and req, including a missing branch

Benefits:

  • Forms with nested fields are readable without bypassing input filtering
  • A form and handler name mismatch fails visibly instead of silently
  • The helper keeps a single source of truth for input access

Technical notes:

  • Purely additive for existing callers: the typed array branch is unchanged
  • The scalar branch reads filter_input(), which has no payload in CLI, so it is verified through real HTTP rather than the probe

Fix: restore comment submission on the frontend
Автор: Eduard Laas | Дата: 12:07 27.07.2026

Posting a comment failed for every visitor: the htmx button carried no CSRF token, so the ajax dispatcher answered with a token mismatch, and the handler read its identifiers from POST while the form sends them in the query string.

Core changes:

  1. Comment form (core/user.php):
  2. Append getPageToken() to the addComment request URL

    • getPageToken() is used instead of getSiteToken() so a cacheable build
receives a signed dynamic-region marker rather than a frozen token
  1. Comment handler (core/user.php):
  2. Read id, cid and mod through getVar('req', ...) like updatePost() does

    • The htmx button sends them as query parameters, not as form fields
  3. Guard the flood check against a first-time visitor

    • strtotime() no longer receives null when the IP has no previous comment

Benefits:

  • Guests and members can post comments again
  • The deprecation notice disappears from the PHP log on every first comment

Technical notes:

  • No schema or route changes; only input reading and the request URL differ
  • Verified through a real guest submission followed by admin moderation
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):
  2. 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
  3. Cap the encoded length before the regex or base64_decode() runs

    • A multi-megabyte payload is refused without allocating a decoded buffer
  4. Force the derived file name to 'image' for data: sources

    • A rejected payload no longer leaks into the alt and title attributes
  5. Cover all three ingestion paths in both safe and unsafe mode

    • Markdown images, BB [img] and persisted raw <img src> repairs
  6. Link contexts (core/classes/parser.php):
  7. 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
  8. Hand data: image sources straight to the whitelist

    • Safe mode previously rewrote every legitimate embed to src="#"
  9. Shared limit and meta images (plugins/editors/toastui/driver.php, core/system.php):

  10. 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
  11. 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):
  2. 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()
  3. 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
  4. Append safety for counters and logs (core/system.php):
  5. 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
  6. Contract test coverage (tests/Support/contract_probe.php, tests/Unit):
  7. 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
  8. Add StatsContractTest and GeoipReaderTest, extend PageCacheContractTest
  9. 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):
  2. updateStatsCookie, updateStatsTrack, marker contract, and page-cache contract comments rewritten concisely within one line each

  3. Test probe (tests/Support/contract_probe.php):
  4. 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):
  2. 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()
  3. 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

  4. Dynamic-region marker contract (core/system.php):
  5. checkDynamicMark() allowlists token scopes ajax/account/scheduler, captcha action login, and positive voting IDs

  6. invalid emitters poison the build, log, and fall back to live rendering
  7. the contract is revalidated at serve time so forged markers stay inert
  8. Fail-safe exact statistics (core/system.php):
  9. v2 stats cookie carries only session metrics and country cache; no client state can suppress an exact count

  10. 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

  11. statistic.log is replaced atomically via temp file and rename under a stable statistic.lock; partial writes can never trigger a day reset

  12. 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

  13. checkUniqueIp()/check_user() consolidated into updateStatsTrack()
  14. Regression coverage (tests/):
  15. 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):
  2. 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
  3. 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
  • 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

  • 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

  • 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)
  • 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

  • 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
  • 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):
  2. 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
  3. 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
  4. Consolidate normal and OAuth login session finalization
  5. Database transaction support (pdo.php):
  6. Add begin, commit, and rollback primitives for shared PDO connections
  7. Normalize transaction exceptions to the existing boolean error contract
  8. Admin output and regression tests (modules/account/admin/index.php, tests/Unit):
  9. Render provider identity data through escaped text template fields
  10. 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

Всего: 1051 на 106 страницах по 10 на каждой странице

1 2 3 4 5 6 7 8 9 10 106
Хотите опробовать SLAED CMS в действии?
Идеи и предложения
Обратная связь
Подтверждение

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