New browse/edit interface for the WordPress install's own files, built
using the installed "WP File Manager" plugin (elFinder-based) as a
functional reference rather than embedding it. Own REST controller
(FileManagerData.php) + Vue page, same pattern as every other sub-app.
Security is the whole point here (highest-risk surface in the plugin —
arbitrary filesystem read/write):
- Root is ABSPATH, manage_options-gated throughout.
- Every client path is resolved via realpath() and checked for root
containment against the *resolved* path, not string-matched against
the raw input — catches symlink tricks and encoded traversal that a
naive "../" check would miss.
- Uploads/new files/renames reject a blocklist of server-executable
extensions (.php, .cgi, .sh, etc.) — the single most important
control, since uploading a webshell is the classic exploit path for
this class of feature.
- Deliberately not built: zip/unzip (zip-slip vulnerability class),
chmod/permissions editing, cross-directory move/copy.
Gated as a paid feature (riskier than Database Explorer, which is
already paid) — a judgment call flagged to the user, not something they
specified.
Also fixed a real bug found while wiring this up: SubApps.php's
page-level lock doesn't stop someone with manage_options from calling
the REST endpoints directly, bypassing the license check entirely.
File Manager's REST permission callbacks now also check
License::is_feature_available(). The same gap exists in Database
Explorer, Role Editor, and Activity Log's REST controllers and is
flagged as a follow-up task, not fixed here (out of scope for what was
asked, affects three unrelated features).
Verified live via direct REST calls: path traversal blocked (404),
blocked extensions rejected (400), root deletion blocked (400), and a
full create/write/read/rename/delete round trip succeeded cleanly.
Co-Authored-By: Claude Sonnet 5 <[email protected]>