Skip to content

feat(privacy): add check for missing wp_privacy_personal_data_erasers registration - #1293

Open
faisalahammad wants to merge 2 commits into
WordPress:trunkfrom
faisalahammad:feature/1252-personal-data-eraser-check
Open

faisalahammad wants to merge 2 commits into
WordPress:trunkfrom
faisalahammad:feature/1252-personal-data-eraser-check

Conversation

@faisalahammad

@faisalahammad faisalahammad commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a new static check (Personal_Data_Eraser_Check) that warns plugin authors when their plugin appears to handle personal data — via add_user_meta, update_user_meta, add_comment_meta, update_comment_meta, or direct $wpdb writes — but does not register a callback via the wp_privacy_personal_data_erasers filter.

Since WordPress 4.9.6, the Personal Data Removal tool lets site administrators honor GDPR erasure requests. Plugins storing personal data are expected to hook into this tool.

Fixes #1252

Changes

includes/Checker/Checks/Plugin_Repo/Personal_Data_Eraser_Check.php (new)

Two-step static check: first confirm the plugin has at least one personal-data storage call, then verify whether it registers the wp_privacy_personal_data_erasers filter. A warning is only emitted when step 1 matches and step 2 does not — avoiding false positives for plugins that never touch personal data.

Detection uses a token_get_all() token scanner, porting the approach from #1292. It matches real function and $wpdb method calls, ignores comments and string literals, and skips files under a top-level tests/ directory.

Key constants:

const PERSONAL_DATA_FUNCTIONS = array( 'add_user_meta', 'update_user_meta', 'add_comment_meta', 'update_comment_meta' );
const WPDB_METHODS            = array( 'insert', 'update', 'replace' );
const ERASER_FILTER           = 'wp_privacy_personal_data_erasers';

The check is experimental and only runs with --include-experimental.

Warning code: missing_personal_data_eraser
Docs link: https://developer.wordpress.org/plugins/privacy/adding-the-personal-data-eraser-to-your-plugin/

includes/Checker/Default_Check_Repository.php

// Added:
'personal_data_eraser' => new Checks\Plugin_Repo\Personal_Data_Eraser_Check(),

Testing

Test 1: Plugin stores user meta, no eraser registered → should warn

  1. Activate a plugin that calls update_user_meta() without hooking wp_privacy_personal_data_erasers
  2. Run Plugin Check against it (Admin UI or WP-CLI)
  3. Result: warning missing_personal_data_eraser is reported ✅

Test 2: Plugin stores user meta and registers an eraser → should pass

  1. Activate a plugin that calls update_user_meta() and registers via add_filter( 'wp_privacy_personal_data_erasers', ... )
  2. Run Plugin Check against it
  3. Result: no missing_personal_data_eraser warning ✅

Test 3: Plugin has no personal data handling → should pass

  1. Run Plugin Check against any plugin that does not call user/comment meta functions or $wpdb writes
  2. Result: no missing_personal_data_eraser warning ✅

Test 4: Direct $wpdb write → should warn

  1. Activate a plugin that calls $wpdb->insert() without registering an eraser
  2. Run Plugin Check with --include-experimental
  3. Result: warning missing_personal_data_eraser is reported ✅

Test 5: Eraser registered only in a tests/ directory → should warn

  1. Activate a plugin that calls update_user_meta() but only registers the eraser inside its own tests/ folder
  2. Run Plugin Check with --include-experimental
  3. Result: warning missing_personal_data_eraser is reported ✅

PHPUnit test class: tests/phpunit/tests/Checker/Checks/Personal_Data_Eraser_Check_Tests.php covers all cases with dedicated test data plugins.

Open WordPress Playground Preview

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_erasers
filter.

- New check class: Personal_Data_Eraser_Check
- Registered in Default_Check_Repository under 'personal_data_eraser'
- PHPUnit test class with three test cases
- Test data plugins (with and without eraser registration)

Fixes WordPress#1252
@github-actions

github-actions Bot commented May 4, 2026

Copy link
Copy Markdown

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 props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: faisalahammad <faisalahammad@git.wordpress.org>
Co-authored-by: dknauss <dpknauss@git.wordpress.org>
Co-authored-by: masteradhoc <masteradhoc@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@dknauss

dknauss commented Aug 9, 2026

Copy link
Copy Markdown

This PR still carries a bug that #1292 already fixed: PERSONAL_DATA_PATTERN never matches $wpdb writes.

'/\b(?:add_user_meta|update_user_meta|add_comment_meta|update_comment_meta|\$wpdb\s*->\s*(?:insert|update|replace))\s*\(/'

The \b sits before the alternation group, so it applies to the \$wpdb branch too. $ is a non-word character, so the boundary only succeeds when $wpdb is preceded by a word character — which never happens in real code:

Input Result
\t$wpdb->insert( $table, $data ); no match
$wpdb->insert( $table, $data ); no match
$wpdb->update( $t, $d, $w ); no match
$ok = $wpdb->replace( $t, $d ); no match
foo$wpdb->insert( $t, $d ); match

So direct DB writes — the case #1252 explicitly calls out — are never detected. The PHPUnit fixtures here don't catch it because neither test plugin exercises a $wpdb write. @AndriusBurba caught the identical bug in #1292 on 4 June, and it was fixed there by replacing the regex with a token_get_all() scanner.

A few other things #1292 resolved that apply equally here, since the two checks share a detection strategy:

Given how closely the two checks mirror each other, it may be simplest to land #1292 first and port its scanner across rather than fix these four things independently.

@faisalahammad

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I ported the token_get_all() scanner from #1292 and fixed all four points.

  • $wpdb writes now match. The check scans real tokens, so $wpdb->insert(), $wpdb->update(), and $wpdb->replace() are detected. There is no regex left.
  • Comments and string literals no longer count as signals. The scanner only looks at real function calls and $wpdb method calls.
  • The check now skips files under a top-level tests/ directory.
  • The check is now experimental and only runs with --include-experimental.

Added four test fixtures that cover each case. The full PHPUnit suite passes (479 tests). Please re-review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Privacy: Add check for wp_privacy_personal_data_erasers filter (GDPR personal data erasure)

2 participants