#52892 closed defect (bug) (fixed)
Privacy Export Personal Data: JSON encoding failure generates invalid JSON in export file
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 5.8 | Priority: | normal |
| Severity: | normal | Version: | 5.4 |
| Component: | Privacy | Keywords: | has-patch has-unit-tests commit |
| Focuses: | Cc: |
Description
When exporting personal data, if the JSON encoding of the groups fails in wp_privacy_generate_personal_data_export_file(), invalid JSON is generated in the generated export.json file:
"{Personal Data Export for tester@test.com:}"
See it in action here https://3v4l.org/bpmqG.
Notice:
- The invalid JSON (as there's no value after the colon
: - No error, warning, or notice is generated for PHP 5.6 to 8.0.3. This makes it difficult to debug as no information is provided as to why the JSON is invalid
Expected Behavior
Either:
- Generate an error via
wp_send_json_errorto alert of the problem and bail out of generating the file - Or generate valid JSON in the file, e.g.
'"false", with a_doing_it_wrongor notice in the error log
What causes JSON encoding to fail?
Found this issue when working on PHPStan type mismatches for when false passed to fwrite. `fwrite` expects a string as it's 2nd argument. This can happen when/if wp_json_encode returns false (i.e. JSON encoding fails).
json_encode returns false when:
- attempting to encode a non-UTF-8 string
WordPress automatically remedies the encoding with _wp_json_sanity_check where it converts string(s) to UTF-8.
- a resource is given
A resource could exist an element of the groups when the post meta data is filtered. Using this test script reproduces the failed JSON encoding and invalid JSON report.
How to Reproduce
- Add this script as a must-use file
- Go to "Tools" > "Export Personal Data" and add a test user in the "Username or email address" field to generate a request.
- Hover over the email and click on "Download personal data"
- Then unzip the downloaded zip file
- Open
export.jsonand notice no value exists in the JSON. For example, if the user's email istester@test.com, theexport.jsonfile would contain:"{Personal Data Export for tester@test.com:}"
Additional Info
It is a type mismatch with fwrite, as a string is expected for the 2nd argument. Fixing this problem guards against future PHP type strictness.
Attachments (1)
Change History (9)
This ticket was mentioned in PR #1115 on WordPress/wordpress-develop by hellofromtonya.
5 years ago
#1
- Keywords has-patch added
#2
@
5 years ago
- Keywords dev-feedback added; has-patch removed
- Milestone changed from Awaiting Review to 5.8
- Owner set to hellofromTonya
- Status changed from new to assigned
Moving into 5.8 milestone for visibility. Removed has-patch as the PR is currently a draft.
Proposing 2 different solutions:
Fix 1:
Send a JSON error
// Convert the groups to JSON format.
$groups_json = wp_json_encode( $groups );
if ( false === $groups_json ) {
wp_send_json_error( __( 'Unable to encode the export file (JSON report).' ) );
}
This fix is breaking change as the files will not be generated or downloaded.
Fix 2:
Fix the type mismatch and then either trigger a notice or warning (to alert of the problem) or _doing_it_wrong.
// Convert the groups to JSON format.
$groups_json = wp_json_encode( $groups );
if ( false === $groups_json ) {
$groups_json = '"false"';
trigger_error( __( 'Unable to encode the export file (JSON report).' ), E_USER_NOTICE );
}
or
// Convert the groups to JSON format.
$groups_json = wp_json_encode( $groups );
if ( false === $groups_json ) {
$groups_json = '"false"';
_doing_it_wrong(
__FUNCTION__,
__( 'Unable to encode the export file (JSON report).' ),
'5.8.0'
);
}
#3
@
5 years ago
- Keywords has-patch has-unit-tests added
Borrowing from REST per conversation in slack, improved Fix 2 to grab the error message and generate a _doing_it_wrong error. PR is updated and ready for review.
#4
@
5 years ago
- Keywords commit added; dev-feedback removed
@SergeyBiryukov the patch for this ticket via PR 1115 is ready for commit.
BTW @jrf and I have reviewed it thoroughly through pair programming sessions. We decided to generate an error instead of fixing the invalid JSON value. Why? Fixing the JSON creates an empty export without knowing why it's empty or what happened. The error provides more context, i.e. the JSON encoding failed.
#5
@
5 years ago
Thanks for the PR!
- It's not quite clear to me why we only append
json_last_error_msg()if there is no error code. In my testing, that leads to not getting any additional information about the error, while the actual error in my case was "Malformed UTF-8 characters, possibly incorrectly encoded". Ifjson_last_error()isJSON_ERROR_NONEor empty, I don't thinkjson_encode()would have returnedfalsein the first place, unless I'm missing something. So I think it would make sense to always includejson_last_error_msg()here. Something like:$error_message = sprintf( /* translators: %s: Error message. */ __( 'Unable to encode the personal data for export. Error: %s' ), json_last_error_msg() );
- Adding a
remove_filter()call to the::tearDown()method is not needed here. I understand that from a consistency point of view it seems like that if we add a filter somewhere, we should remove it as well, however the baseWP_UnitTestCase_Base::tearDown()method already handles that for us via::_restore_hooks(), see [29251] / #28535 for more details. So removing the filter ourselves is redundant, similar instances were previously removed in [50463] / #52625.
52892.diff includes these suggestions.
#6
@
5 years ago
@SergeyBiryukov great points! You're right on both. Including the last message does provide more context for debugging. Thanks. PR updated though I see the diff you supplied has the same changes.
hellofromtonya commented on PR #1115:
5 years ago
#8
Committed with changeset https://core.trac.wordpress.org/changeset/50713
Trac ticket: https://core.trac.wordpress.org/ticket/52892
Function:
wp_privacy_generate_personal_data_export_file()PHPStan flagged a type mismatch if
falseis passed intofwritefor groups JSON:{{{php
fwrite( $file, $groups_json );
}}}
The groups JSON is assigned from
wp_json_encode:{{{php
$groups_json = wp_json_encode( $groups );
}}}
The return signatures of `wp_json_encode` are
stringorfalse:### Is Passing
falsea problem?See it in action here https://3v4l.org/bpmqG
falsewrites nothing into the file:{{{json
"{Personal Data Export for tester@…:}"
}}}
This is the current behavior 👆, where generating invalid JSON is a bug.
### What can cause JSON encoding to fail?
json_encodereturnsfalsewhen:WordPress automatically remedies the encoding with
_wp_json_sanity_checkwhere it converts string(s) to UTF-8.A resource could exist an element of the groups _when_ the post meta data is filtered. Using this test script reproduces the failed JSON encoding and invalid JSON report.