Pixellint 0.39.0 checks PHP parsing and sanitization in Matomo bulk requests.
Bulk tracking needs the receiver’s parsing rules.
A Matomo bulk request wraps tracking entries in a JSON requests collection. The receiver can process a query-string entry or a native map, then read the resulting fields through Matomo’s own helpers. Looking only for a nonempty idsite and rec in the original text misses cases where the PHP parser creates an array or a reader substitutes its documented default. The value used by the tracker can differ from the apparent value in the pasted request.
Pixellint already reused its event and ecommerce contracts for observable bulk entries. This release extends the bounded matomo_php8 profile that prepares those entries. It adds bracket trees, the pinned Common sanitization steps and native readers for core plugin flags. It also corrects a false positive: optional browser capability flags can report zero, as Matomo’s official JavaScript tracker does. These changes deepen an existing pack without adding a vendor.
The destination scope remains the documented matomo.php and piwik.php routes, including self-hosted collectors. A complete HTTP capture gives the validator the body and the availability context needed to inspect its bulk entries. Each inspected entry keeps its own collection location, so a malformed second item is reported against that item rather than against the first event or the surrounding request.
Bracket fields can change which defaults apply.
PHP bracket syntax creates parameter trees. Empty brackets append members, numeric brackets address indexes, and named brackets create keyed members. Pixellint 0.39.0 follows the supported forms before running the mapped Matomo readers. This matters for ordinary required fields as well as nested data: idsite[]=1 creates a container, while idsite=1 creates a scalar website ID.
In the pinned implementation, the integer reader for idsite falls back to zero for the array form. The pack can therefore report that the resulting site ID does not satisfy its existing positive-ID contract. Similarly, rec[]=1 does not become the recording flag merely because the digit appears inside the original string. The recording reader’s default reaches the existing rec=1 check.
Nested JSON-oriented fields receive their own handling. Supported bracket trees for ec_items and uadata preserve the distinction between indexed members and named members as they reach the mapped reader. The local implementation keeps bounds around these structures. Recognizing a supported nested form does not mean that arbitrary PHP syntax or every custom tracking field has acquired a destination contract.
Later assignments determine the surviving value.
The order of duplicate assignments matters to the receiver. A scalar can replace an earlier tree, and a later bracket assignment can replace a scalar with a container. The parser models that shadowing for supported bulk query forms instead of validating every discarded value as though the tracker used them all. Required-field checks run on the value that survives parsing and the mapped reader.
Consider idsite=0&idsite[]=1&idsite=2&rec=1. The final scalar assignment leaves the website ID at 2, so the earlier zero and array do not create an extra site-ID error. Reverse the final assignments and the surviving type changes. Those pairs belong in regression controls because a parser that always keeps the first value or always flattens containers can produce a plausible but incorrect result.
Top-level name normalization also participates in this process. PHP can normalize a space or dot in a field name to an underscore, allowing apparently different spellings to target the same key. Percent decoding happens at the appropriate input layer. The implementation does not repeatedly decode escaped content until it resembles a familiar field or a valid number.
Sanitization belongs before the mapped field contract.
The pinned Matomo Common helper sanitizes input as part of reading a request variable. Pixellint now models the supported HTML4 entity decoding, quote escaping and null-byte removal for that profile. This closes the previous blanket deferral for sanitizer-sensitive values inside otherwise inspectable bulk entries. The result is evaluated by the destination field reader rather than by a generic guess about its appearance.
An encoded numeric entity illustrates why the order matters. The bulk query value idsite=%26%2349%3B decodes to the entity spelling for the digit 1. The pinned sanitization and integer reader can resolve that input to the same site ID as a scalar 1. A validator that applies its integer format check to the entity spelling would reject the value too early.
The controls also cover named entities, encoded quotes, null bytes and nested entity spellings. They distinguish a single supported decoding pass from recursive decoding. Invalid UTF-8 remains outside this profile, and a sanitized value is not an authentication or account-configuration verdict. The behavior here describes how the reviewed local reader prepares an input for existing tracking contracts.
Runtime-dependent forms retain explicit limits.
The expanded profile has a defined PHP input context. It uses ampersand separators, a 1,000-variable limit and a 64-level bracket bound. Literal semicolon separator candidates remain unvalidated because a deployment can configure separator handling differently. Inputs beyond the supported variable or nesting bounds also retain an explicit deferral. These are local evaluation limits, not a statement that every Matomo collector rejects those requests.
Malformed bracket syntax and numeric indexes whose meaning is not portable across the supported integer context remain outside the checked subset. Append behavior after negative-only indexes also has PHP-version dependencies, so the implementation does not claim a universal result there. Ordinary named and portable numeric trees can be inspected without extending that claim to every possible key.
The apiv tracking field does not identify the deployed PHP version or Matomo plugin set. The source pin makes the reviewed behavior reproducible; it does not discover those deployment facts from a request. The depth audit records remaining gaps alongside the implemented mapping so that a later expansion can target an exact reader or runtime condition instead of treating a clean local report as complete server compatibility.
The expectations come from an independent PHP execution.
The new parser and reader controls use an executed PHP 8.3.35 runtime with the pinned Matomo source at commit 1e9169ddd9eba7c982dc67b8bd3f9310a7a312c6. The recorded evidence includes runtime settings and source hashes. PHP produces the parsed trees and the Common reader outputs. Pixellint’s candidate results are compared with those independent observations.
The fixture set includes successful requests, deliberate field errors, surviving duplicate assignments and excluded runtime boundaries. Entity controls check both the parsed input and its reader result. Native-map controls exercise typed values. These expectations are independent of Pixellint’s own output, which prevents a parser implementation from becoming its own acceptance oracle.
Executing that source locally provides stronger evidence for the modeled conversions than a checklist of fixture names. It still does not execute a customer’s collector, verify a configured site ID, or establish attribution. The PHP runtime is a validation oracle used for the release review. Pixellint continues to ship its local engine through the native and WASM distributions.
Sources
- Pixellint 0.39.0 release
- Aggregate comparison of the frozen corpus
- Matomo Tracking HTTP API
- Pinned Matomo bulk request processing
- Pinned Matomo field readers and sanitization
- Pinned Matomo tracker defaults and plugin flags
- Pinned Matomo JavaScript capability-flag producer
- PHP query parsing and input-variable limits
- Independent PHP execution evidence and regression controls
- Implemented depth and remaining source limits
Contract pages
The dated argument is above. These pages are the field lists.