fix(mapping): a PHP method with a visibility modifier is a region

`^\s*function` matched a bare declaration and nothing else, so every method
written the way modern PHP writes them -- `public function`, `protected
function`, `private static function` -- was invisible to the region model.

The effect is not subtle on class-based plugins. MW WP Form's
`class.mail.php` produced ONE file-level region spanning 272 lines instead
of 16 method-scoped ones, and its `protected function _delete_files()` --
which holds CVE-2023-6559 -- was not a region at all. Paid Memberships Pro
happened to work only because that particular file omits the modifiers.

  class.mail.php   1 file-level region -> 16 method-scoped regions

Also adds the MW WP Form checkout as the PATH family's plugin, pinned at
3bdd33d (5.0.3) with CVE-2023-6559 verified in the code:

    protected function _delete_files() {
        foreach ( $this->attachments as $file ) {
            if ( file_exists( $file ) ) {
                unlink( $file );

and the fix at 02ebc67 replacing the existence check with `realpath()`,
`is_file()` and a directory-confinement test. `unlink` is a modelled path
sink and `realpath` a modelled sanitizer, so this is a pair the engine
should separate rather than merely flag -- which is what makes it worth
more than a single hit.

Gates byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
Marius Ciepluch
2026-09-09 17:33:36 +02:00
co-authored by Claude Opus 5
parent f13934c663
commit 37a6594703
3 changed files with 70 additions and 1 deletions
+27
View File
@@ -332,3 +332,30 @@ def test_an_array_callable_is_a_handler_too(tmp_path: Path) -> None:
assert by_name["get_order"].kind == "route", "an array callable registers a route"
assert by_name["get_order"].access_level == "authenticated", "its permission_callback is not __return_true"
assert by_name["fetch"].access_level == "public", "nopriv, through an array callable"
def test_a_php_method_with_a_visibility_modifier_is_a_region(tmp_path: Path) -> None:
"""Modern PHP always writes `public`/`protected`/`private` before `function`.
Matching only a bare `function` made every class-based plugin ONE file-level region: MW WP Form's
`protected function _delete_files()` was invisible, and its file went from 16 method-scoped regions to a
single one spanning everything. Paid Memberships Pro happened to work only because that file omits the
modifiers.
"""
from openultrasast.mapping import analyze_entry_points
from openultrasast.preprocess import preprocess_repository
(tmp_path / "svc.php").write_text(
"<?php\n"
"class Service {\n"
" public function open($p) { return fopen($p, 'r'); }\n"
" protected function _delete_files($f) { unlink($f); }\n"
" private static function helper($x) { return $x; }\n"
" function bare($y) { echo $y; }\n"
"}\n"
)
_, targets = preprocess_repository(tmp_path)
names = {entry.function_name for entry in analyze_entry_points(tmp_path, targets)}
assert {"open", "_delete_files", "helper", "bare"} <= names