Skip to content

Minor dev-branch findings (tracker for L1-L9 from review of last 3 months) #2246

@adrianbj

Description

@adrianbj

Short description of the issue

Single tracker for nine low-severity findings from a scan of dev over the last 3 months (commits between 2026-02-12 and 2026-05-12). Each is a small bug or behavior change — none are showstoppers, but logging them here so they don't get lost. HEAD 15c749ed.


L1 — sessionCacheLimiter admin-URL detection is prefix-only

Commit: ae20ea03 · File: wire/core/Session/Session.php:574-581
strpos($_SERVER['REQUEST_URI'], $url) === 0 matches URLs like /administrators as admin when the admin URL is /admin. Wrong cache-context header gets selected (perf only, not a security issue).
Fix: boundary check via rtrim($url, '/') . '/'.

L2 — URL sanitizer authority no longer normalized

Commit: 1f8b2726 · File: wire/core/Sanitizer/Sanitizer.php:2362-2375
The encode/decode dance is skipped for the authority portion. URLs with extended characters in userinfo/host that previously round-tripped through FILTER_VALIDATE_URL may now be rejected. Narrow functional regression; no security implication.

L3 — Session::getIP() unsanitized REMOTE_ADDR fallback

Commit: 36e11a60 · File: wire/core/Session/Session.php:1085
When all X-Forwarded-For entries are invalid, falls back to raw $_SERVER['REMOTE_ADDR'] without re-detecting $ipv6 — an IPv6 REMOTE_ADDR can then be sliced with IPv4 logic, producing a malformed partial-IP string in the fingerprint. False session invalidation, not exploitation.

L4 — Paths::short() blindly slices non-matching paths

Commit: bf3d7119 · File: wire/core/Paths.php
substr($value, strlen($root)-1) — when the caller passes a path not under $root, returns a wrong substring silently. Should verify strpos($value, $root) === 0 first and return $value unchanged otherwise.

L5 — WireDataDB::getCache() can clobber existing non-cache meta

Commit: 008ffb5a · File: wire/core/WireDataDB.php
If a meta key contains a non-cache array (no _cre/_exp/_val), decodeCacheValue() returns null and getCache() interprets that as "expired," overwriting the stored value with a cache envelope. Users converting an existing meta key to getCache() silently lose their data.

L6 — FieldtypeComments::getCommentsFields($one=true) index-type change

Commit: 7375d28b (incidental) · File: wire/modules/Fieldtype/FieldtypeComments/FieldtypeComments.module
Internal switch from numerically-indexed to name-indexed array. reset() fixes the $one=true internal path, but any external caller doing $fields[0] on the returned array now gets null.

L7 — PageFinder v2: self::$level not decremented on exception

Commit: 01d6cc00 · Files: wire/core/PageFinder/PageFinder.php:817, wire/core/PageFinder/PageFinder2.php:962
self::$level++ at start of ___find() matched by self::$level-- only on the normal-return paths. Exceptions leave the counter incremented for the rest of the request, suppressing testMode timing.
Fix: wrap body in try { ... } finally { self::$level--; ... }.

L8 — PageFinder v2: getInstance::$settings cache not keyed by Wire instance

Commit: 01d6cc00 · File: wire/core/PageFinder/PageFinder2.php:4084-4087
Static $settings cached on first call; multi-instance ProcessWire setups silently use the first instance's config->PageFinder for all instances. Multi-instance is uncommon but supported.

L9 — status=<published operator translations are counter-intuitive

Commit: abd4bd4b · Files: wire/core/PageFinder/PageFinder.php:701, wire/core/PageFinder/PageFinder2.php:822
The lenient status=published translation also rewrites <, <=, >, >=. status<published becomes status>=unpublished (matches hidden + unpublished + trash), which is the opposite of any reasonable reading. The original ask was leniency on =published typos; the comparison-operator translations weren't part of that. Suggest restricting the table to = and != only.


Setup/Environment

  • ProcessWire version: dev @ 15c749ed
  • Scan range: 2026-02-12 → 2026-05-12 (~80 PHP-touching commits)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions