feat(privacy): add check for missing wp_privacy_personal_data_exporters registration - #1292
faisalahammad wants to merge 7 commits into
Conversation
Add a new static check that warns plugin authors when their plugin handles personal data (user meta, comment meta, direct DB writes) but does not register a callback via the wp_privacy_personal_data_exporters filter. - New check class: Personal_Data_Exporter_Check - Registered in Default_Check_Repository under 'personal_data_exporter' - PHPUnit test class with three test cases - Test data plugins (with and without exporter registration) Fixes WordPress#1251
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Thanks for tackling this, @faisalahammad! I tested the branch locally in wp-env against a few plugins. The
False positives from matching raw text. Because the check runs regex over file contents rather than tokens: (a) a plugin that mentions Scope. Even after the Minor: the |
- switch to token scanner to skip comments and match $wpdb correctly - add experimental trait to avoid noisy stable warnings - exclude plugin's own tests/ directory - fix @SInCE tags to 2.0.0 - add docs/checks.md row and new wpdb-insert test case
|
Thanks for the detailed review. Pushed fixes for everything you flagged. Big ones:
Minor:
Also added a 4th PHPUnit test for the $wpdb->insert case and a matching testdata plugin. All 476 phpunit tests pass, phpcs clean, phpstan clean, coderabbit review clean. Let me know if anything else needs a look. |
- extract is_personal_data_function_call helper to reduce NPath complexity - extract is_wpdb_method_call helper to reduce NPath complexity - extract is_exporter_filter_registration helper to reduce NPath complexity Errors fixed: - PHPMD NPathComplexity: find_file_with_personal_data_signal() from 3154 to < 50 - PHPMD NPathComplexity: plugin_registers_exporter() from 1300 to < 50 - PHPMD CyclomaticComplexity: find_file_with_personal_data_signal() from 21 to < 10 PHP 7.4+ compatible. All CI checks passing.
|
Hey @AndriusBurba - did you had the chance to recheck this PR once again? |
|
The move to a These were probably not intended for inclusion in the diff:
Separately, #1293 implements the eraser half of this and still uses the original regex — including the |
# Conflicts: # includes/Checker/Default_Check_Repository.php
|
Thanks @dknauss — all three stray files are now removed from the diff:
Removed in Agreed on #1293 — landing this token-scanner version first and porting it across to the eraser half would retire the |
|
Quick follow-up @AndriusBurba — in my earlier reply I noted Recap of the state vs your review, all in the current head
Happy to adjust if anything else needs a look. |
There was a problem hiding this comment.
Pull request overview
Adds an experimental privacy check that warns when plugins store personal data without registering a WordPress personal-data exporter.
Changes:
- Implements token-based personal-data and exporter detection.
- Registers and documents the new check.
- Adds PHPUnit fixtures covering key scenarios.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
includes/Checker/Checks/Plugin_Repo/Personal_Data_Exporter_Check.php |
Implements the check. |
includes/Checker/Default_Check_Repository.php |
Registers the check. |
docs/checks.md |
Documents the check. |
tests/phpunit/tests/Checker/Checks/Personal_Data_Exporter_Check_Tests.php |
Adds PHPUnit coverage. |
tests/phpunit/testdata/plugins/test-plugin-personal-data-exporter-with-errors/load.php |
Provides missing-exporter fixture. |
tests/phpunit/testdata/plugins/test-plugin-personal-data-exporter-without-errors/load.php |
Provides registered-exporter fixture. |
tests/phpunit/testdata/plugins/test-plugin-personal-data-exporter-with-wpdb-insert/load.php |
Provides direct-database-write fixture. |
Suppressed comments (1)
includes/Checker/Checks/Plugin_Repo/Personal_Data_Exporter_Check.php:326
$open_parencan benull, but it is passed to the typed helper before this condition checks it. An incomplete file ending withadd_filtertherefore crashes the entire check with aTypeError. Validate the opening parenthesis before looking up the argument.
$open_paren = $this->get_next_significant_token_index( $tokens, $index );
$arg_index = $this->get_next_significant_token_index( $tokens, $open_paren );
if ( null === $open_paren || '(' !== $tokens[ $open_paren ] || null === $arg_index ) {
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- fix tests/ exclusion match on plugin directory path
- skip comments when finding next significant token
- match fully-qualified function calls across PHP 7.4/8.0
- require '(' for wpdb method calls (reject property reads)
- null-check token lookups before use
|
All 5 Copilot review findings addressed in
All quality gates pass: phpcs 0 errors, phpstan 0 errors, PHPUnit 496/496 (including 4 Ready for re-review. |
Summary
Adds a new static check,
Personal_Data_Exporter_Check, that warns plugin authors when their plugin stores personal data (user meta, comment meta, or direct DB writes) but does not register an exporter callback via thewp_privacy_personal_data_exportersfilter. Since WordPress 4.9.6, plugins handling personal data are expected to hook into the Personal Data Export tool so site admins can fulfill GDPR data export requests.Fixes #1251
Changes
includes/Checker/Checks/Plugin_Repo/Personal_Data_Exporter_Check.php(new)A token-based static file check (
token_get_all(), so comments and string literals are ignored) with a two-step flow:add_user_meta,update_user_meta,add_comment_meta,update_comment_meta, and$wpdb->insert/update/replace). Files under the plugin's owntests/directory are excluded.add_filter( 'wp_privacy_personal_data_exporters', ... )— if absent, emit a severity-5 warning (missing_personal_data_exporter).The check is marked experimental, so it only runs with
--include-experimental. This keeps the default run quiet because common non-personal uses of these functions (view counters, plugin settings, caches) may otherwise produce false positives.includes/Checker/Default_Check_Repository.phpRegisters the check under the
Plugin_Repocategory so it runs alongside other plugin directory compliance checks:Testing
Test 1: Plugin stores user meta, no exporter registered (expects warning)
update_user_meta()with no exporter filter--include-experimentalmissing_personal_data_exporterappears ✅Test 2: Plugin stores user meta, exporter registered (expects clean)
update_user_meta()and registersadd_filter( 'wp_privacy_personal_data_exporters', ... )--include-experimentalmissing_personal_data_exporterwarning ✅Test 3: Plugin has no personal data handling (expects clean)
--include-experimentalmissing_personal_data_exporterwarning ✅Test 4: Plugin uses
$wpdb->insert()with no exporter registered (expects warning)$wpdb->insert()with no exporter filtermissing_personal_data_exporterwarning appears ✅PHPUnit tests added at
tests/phpunit/tests/Checker/Checks/Personal_Data_Exporter_Check_Tests.phpcovering all four scenarios.AI Usage Disclosure
AI tools were used: Claude Code assisted with the token-scanner implementation, applying review feedback (replacing the initial regex with a token-based scanner, experimental opt-in, test-path exclusions), and writing/updating the PHPUnit test coverage.