Files
Open_Ultra_SAST/benchmarks
Marius CiepluchandClaude Opus 5 dcdbe322a1 feat(cpg): taint a field where it is assigned — the field half of 5.11
`_delete_files()` takes no parameters. CVE-2023-6559 arrives as a FIELD,
written by one method and read by another:

    public function __construct( ..., array $attachments = array() )
        $this->attachments = $attachments;          // half one ends here
    protected function _delete_files()
        foreach ( $this->attachments as $file )     // half two starts here
            unlink( $file );

Both halves were already in the graph. Only the join was missing, and the key
it joins on is the literal `$this->attachments`, written identically at both
ends -- so this is a second question asked with the query that already exists,
not an edge a frontend has to invent.

A read of `$this->F` is a source when some assignment to the same literal
`$this->F` in the same file is fed by an unsanitized flow from a seed. Scoped
three ways so a field name common to every class ever written cannot become a
wormhole: only fields the sink scope reads, only assignments in the same file,
and the right-hand side must itself be reachable from a source.

The receiver could then leave `parameterNodes` -- the precision fix refused
two commits ago, now sound, because the path it was standing in for is carried
by the field it actually travels through:

                          before                     after
  mwwpform vulnerable     1, from `this`             1, from $this->attachments
  mwwpform fixed          0                          0
  pmpro vulnerable        15 -- 4 named, 11 receiver 18 -- all named or field
  pmpro fixed             1, receiver-sourced        1
  VAmPI                   4 entailed                 4 entailed, unchanged

CVE-2023-23488 is still at class.memberorder.php:936 and CVE-2023-6559 at
class.mail.php:259. Every finding now names the value it followed instead of
naming the object.

Three things cost a measurement each, recorded in the task:

  * A summary must carry the SANITIZATION status of the half it summarises,
    not merely its reachability. Without that clause,
    `$this->sqlQuery = "..." . esc_sql($x) . "..."` reads as tainted -- and
    PMPro builds most of its queries that way, so the fixed side went from 1
    finding to 13. That is the pair no longer separating, which is the only
    number that matters.
  * Two-phase `reachableBy` then `reachableByFlows` is SLOWER, not faster:
    183s -> 256s. Running the analysis twice costs more than skipping path
    reconstruction saves. Reverted.
  * Memoizing per field rather than per file took it to 97s, about 1.6x the
    pre-field cost for that class, because only fields some region reads are
    worth the flow query.

`inScope` is factored out of `sinkCalls` so the field sources are scoped to
exactly the same place as the sinks, rather than a second copy of those three
branches drifting from the first.

841 tests pass; gates byte-identical; VAmPI unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
2026-09-09 19:42:20 +02:00
..