Skip to content

Cookie Consent: register WP personal-data exporter/eraser for the consent log#49852

Open
chihsuan wants to merge 13 commits into
trunkfrom
wooa7s-1563-cookie-consent-privacy
Open

Cookie Consent: register WP personal-data exporter/eraser for the consent log#49852
chihsuan wants to merge 13 commits into
trunkfrom
wooa7s-1563-cookie-consent-privacy

Conversation

@chihsuan

@chihsuan chihsuan commented Jun 23, 2026

Copy link
Copy Markdown
Member

Fixes WOOA7S-1563

Proposed changes

The cookie-consent log stores a visitor's IP address and WordPress user ID but registered no personal-data exporter/eraser, so a site owner had no way to fulfil a GDPR data-subject (export/erasure) request against it. This is a legally-blocking gap for a GDPR-targeted feature.

Why

  • GDPR requires that a site owner can export and erase the personal data they hold for a data subject. The consent-log table held personal data (IP + user link) with no way to surface or remove it through WordPress' built-in Tools → Export/Erase Personal Data flow.

How

  • New Consent_Log_Privacy class registers wp_privacy_personal_data_exporters and wp_privacy_personal_data_erasers, matching rows by email → user ID and paginating 100 rows per page. It's booted from Consent_Log_Controller::init().
  • The exporter returns the subject's consent-log rows (consent ID, event, IP, URL, consent types, GMT date) under a "Cookie Consent Log" group.
  • The eraser defaults to anonymizing matched rows (clears the IP and detaches the user, keeping an anonymized record for consent accountability) and reports items_removed + items_retained accordingly. A jetpack_cookie_consent_erase_mode filter lets a site opt into hard deletion.

What

  • Registers a personal-data exporter and eraser for the consent log, wired into core's privacy tools.
  • Renames the consent-log customer_id column to user_id (the value is get_current_user_id(); this package is plugin-agnostic and the old WooCommerce-flavoured name was misleading). The package is unreleased, so there is no runtime migration — a fresh install creates the user_id column directly. See "Reviewers with a pre-rename install" below if you tested an earlier build.
  • Adds the package's first PHPUnit suite (11 tests) covering the exporter, eraser (anonymize + hard-delete + >100-row pagination convergence), and the fresh-install schema.

Known limitation: guest rows (user_id = 0, no email stored) are out of scope for core's email-keyed tools; that data is covered by the existing 30-day retention cleanup.

Related product discussion/links

Does this pull request change what data or activity we track or use?

No new data is collected. This PR adds the ability to export and erase existing consent-log data via WordPress' privacy tools, and renames an existing column (customer_iduser_id). The eraser's default mode anonymizes (clears IP, detaches the user) rather than deleting, retaining an anonymized consent record for accountability. This is a privacy-positive change.

Testing instructions

Manually verified on chi.jurassic.tube, and covered by the package's PHPUnit suite (cd projects/packages/cookie-consent && composer phpunit → 11 tests passing).

Reviewers with a pre-rename install: because the package is unreleased there is no automatic migration. If you installed a build before this PR, your consent-log table still has the old customer_id column. Run this one-off, data-preserving rename before testing (it mirrors the new schema):

wp db query "ALTER TABLE \`$(wp db prefix)jetpack_cookie_consent_logs\` CHANGE customer_id user_id bigint(20) UNSIGNED NOT NULL DEFAULT 0"

Or, since this is throwaway alpha data, just drop the table and reset the schema version so it recreates fresh on next load:

wp db query "DROP TABLE IF EXISTS \`$(wp db prefix)jetpack_cookie_consent_logs\`"
wp option delete jetpack_cookie_consent_consent_log_db_version

A fresh install needs none of this.

On a site with the package active and at least one consent-log row for a logged-in user:

  1. Export — Go to Tools → Export Personal Data, enter the user's email, send & confirm the request, then download the archive. Verify it contains a "Cookie Consent Log" group with the expected fields (Consent ID, Event, IP Address, URL, Consent Types, Date (GMT)).
  2. Erase (default anonymize) — Go to Tools → Erase Personal Data, enter the same email, confirm. Verify the admin notice reports data was removed/retained, and that the row(s) survive but are anonymized (IP cleared, user_id set to 0).
  3. Erase (hard delete, optional) — Add a filter add_filter( 'jetpack_cookie_consent_erase_mode', fn() => 'delete' ); and repeat step 2; verify the matched rows are deleted.
  4. Guest rows — Confirm an email-based request leaves guest rows (user_id = 0) untouched (documented limitation).
Screenshot 2026-06-23 at 3 41 58 PM

@jp-launch-control

jp-launch-control Bot commented Jun 23, 2026

Copy link
Copy Markdown

Code Coverage Summary

This PR did not change code coverage!

That could be good or bad, depending on the situation. Everything covered before, and still is? Great! Nothing was covered before? Not so great. 🤷

Full summary · PHP report

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!


Premium Analytics plugin:

No scheduled milestone found for this plugin.

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

chihsuan added 3 commits June 23, 2026 15:14
Gate items_removed/done on the actual $wpdb->query result so a failed
UPDATE/DELETE no longer reports success and can't wedge core's erasure
loop for >100-row subjects. Add delete-mode convergence, guest-row, and
REST user_id-filter tests; fix stale base TestCase docblock.
@chihsuan chihsuan requested review from a team as code owners June 23, 2026 07:29
@github-actions github-actions Bot added [Plugin] Premium Analytics [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. and removed [Status] Needs Review This PR is ready for review. labels Jun 23, 2026
@chihsuan chihsuan requested a review from Copilot June 23, 2026 07:45
@chihsuan chihsuan added [Status] Needs Review This PR is ready for review. and removed [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. labels Jun 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds GDPR support to the cookie-consent package’s consent-log by wiring it into WordPress core’s personal data export/erasure tools, while also standardizing the schema naming (customer_iduser_id) and introducing the package’s first PHPUnit test suite.

Changes:

  • Register a personal-data exporter and eraser for consent-log rows (export by email→user, erase via anonymize-by-default with optional hard delete).
  • Rename consent-log schema + REST filtering from customer_id to user_id.
  • Add PHPUnit/WorDBless-based test scaffolding and coverage for exporter/eraser behavior and schema expectations.

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
projects/plugins/premium-analytics/composer.lock Updates locked metadata for the cookie-consent path dependency, reflecting added dev tooling/scripts.
projects/plugins/premium-analytics/changelog/update-cookie-consent-lock Changelog entry for the lockfile update.
projects/packages/cookie-consent/composer.json Adds PHP test tooling (phpunit-select-config, test environment, polyfills) and test scripts.
projects/packages/cookie-consent/.gitattributes Excludes tests/phpunit config from production builds.
projects/packages/cookie-consent/changelog/add-consent-log-privacy-exporter-eraser Changelog entry for privacy exporter/eraser and schema rename.
projects/packages/cookie-consent/src/class-consent-log-controller.php Boots privacy integration and renames schema/REST filtering to user_id; exposes table name for reuse.
projects/packages/cookie-consent/src/class-consent-log-privacy.php New privacy exporter/eraser implementation for consent-log rows.
projects/packages/cookie-consent/tests/.phpcs.dir.xml Enables Jetpack test PHPCS ruleset for the new test suite.
projects/packages/cookie-consent/tests/php/bootstrap.php Initializes the Jetpack test environment (sqlite/WorDBless) for this package.
projects/packages/cookie-consent/tests/php/class-testcase.php Shared base TestCase and DB helpers for consent-log tests.
projects/packages/cookie-consent/tests/php/Scaffold_Test.php Smoke test confirming the test environment is wired correctly.
projects/packages/cookie-consent/tests/php/Consent_Log_Schema_Test.php Verifies fresh-install schema uses user_id (not customer_id).
projects/packages/cookie-consent/tests/php/Consent_Log_Privacy_Exporter_Test.php Tests exporter behavior (unknown email, user match, pagination, guest rows).
projects/packages/cookie-consent/tests/php/Consent_Log_Privacy_Eraser_Test.php Tests eraser behavior (anonymize default, delete mode, convergence, guest rows).
projects/packages/cookie-consent/tests/php/Consent_Log_Controller_Rest_Test.php Tests REST filtering behavior for user_id and ensures customer_id is ignored.
projects/packages/cookie-consent/phpunit.8.xml.dist PHPUnit config for older runners.
projects/packages/cookie-consent/phpunit.9.xml.dist PHPUnit config for older runners.
projects/packages/cookie-consent/phpunit.11.xml.dist PHPUnit config for newer runners.
projects/packages/cookie-consent/phpunit.12.xml.dist PHPUnit config for newer runners.

Comment on lines 103 to +107
add_action( 'rest_api_init', array( $instance, 'register_routes' ) );
add_action( self::CLEANUP_HOOK, array( $instance, 'cleanup_expired_logs' ) );

Consent_Log_Privacy::init();

Comment on lines +86 to +101
private static function get_rows( $user_id, $page ) {
global $wpdb;
$table = Consent_Log_Controller::get_table_name();
$offset = ( max( 1, $page ) - 1 ) * self::PER_PAGE;
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
return $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$table} WHERE user_id = %d ORDER BY id LIMIT %d OFFSET %d",
$user_id,
self::PER_PAGE,
$offset
),
ARRAY_A
);
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants