Ticket #43545: 43545.5.diff
File 43545.5.diff, 17.0 KB (added by , 7 years ago) |
---|
-
src/wp-admin/includes/class-wp-community-events.php
diff --git src/wp-admin/includes/class-wp-community-events.php src/wp-admin/includes/class-wp-community-events.php index fe16dc46e4..77bf34d813 100644
class WP_Community_Events { 234 234 */ 235 235 public static function get_unsafe_client_ip() { 236 236 $client_ip = false; 237 $ip_prefix = '';238 237 239 238 // In order of preference, with the best ones for this purpose first. 240 239 $address_headers = array( … … class WP_Community_Events { 265 264 return false; 266 265 } 267 266 268 // Detect what kind of IP address this is. 269 $is_ipv6 = substr_count( $client_ip, ':' ) > 1; 270 $is_ipv4 = ( 3 === substr_count( $client_ip, '.' ) ); 267 $anon_ip = wp_privacy_anonymize_ip( $client_ip, true ); 271 268 272 if ( $is_ipv6 && $is_ipv4 ) { 273 // IPv6 compatibility mode, temporarily strip the IPv6 part, and treat it like IPv4. 274 $ip_prefix = '::ffff:'; 275 $client_ip = preg_replace( '/^\[?[0-9a-f:]*:/i', '', $client_ip ); 276 $client_ip = str_replace( ']', '', $client_ip ); 277 $is_ipv6 = false; 278 } 279 280 if ( $is_ipv6 ) { 281 // IPv6 addresses will always be enclosed in [] if there's a port. 282 $left_bracket = strpos( $client_ip, '[' ); 283 $right_bracket = strpos( $client_ip, ']' ); 284 $percent = strpos( $client_ip, '%' ); 285 $netmask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000'; 286 287 // Strip the port (and [] from IPv6 addresses), if they exist. 288 if ( false !== $left_bracket && false !== $right_bracket ) { 289 $client_ip = substr( $client_ip, $left_bracket + 1, $right_bracket - $left_bracket - 1 ); 290 } elseif ( false !== $left_bracket || false !== $right_bracket ) { 291 // The IP has one bracket, but not both, so it's malformed. 292 return false; 293 } 294 295 // Strip the reachability scope. 296 if ( false !== $percent ) { 297 $client_ip = substr( $client_ip, 0, $percent ); 298 } 299 300 // No invalid characters should be left. 301 if ( preg_match( '/[^0-9a-f:]/i', $client_ip ) ) { 302 return false; 303 } 304 305 // Partially anonymize the IP by reducing it to the corresponding network ID. 306 if ( function_exists( 'inet_pton' ) && function_exists( 'inet_ntop' ) ) { 307 $client_ip = inet_ntop( inet_pton( $client_ip ) & inet_pton( $netmask ) ); 308 } 309 } elseif ( $is_ipv4 ) { 310 // Strip any port and partially anonymize the IP. 311 $last_octet_position = strrpos( $client_ip, '.' ); 312 $client_ip = substr( $client_ip, 0, $last_octet_position ) . '.0'; 313 } else { 269 if ( '0.0.0.0' === $anon_ip || '::' === $anon_ip ) { 314 270 return false; 315 271 } 316 272 317 // Restore the IPv6 prefix to compatibility mode addresses. 318 return $ip_prefix . $client_ip; 273 return $anon_ip; 319 274 } 320 275 321 276 /** -
src/wp-includes/functions.php
diff --git src/wp-includes/functions.php src/wp-includes/functions.php index 3f11cce7e0..adc88fb2fe 100644
All at ###SITENAME### 6127 6127 ), $email_change_email['message'], $email_change_email['headers'] 6128 6128 ); 6129 6129 } 6130 6131 /** 6132 * Return an anonymized IPv4 or IPv6 address. 6133 * 6134 * @since 5.0.0 Abstracted from `WP_Community_Events::get_unsafe_client_ip()`. 6135 * 6136 * @param string $ip_addr The IPv4 or IPv6 address to be anonymized. 6137 * @param bool $ipv6_fallback Optional. Whether to return the original IPv6 address if the needed functions 6138 * to anonymize it are not present. Default false, return `::` (unspecified address). 6139 * @return string The anonymized IP address. 6140 */ 6141 function wp_privacy_anonymize_ip( $ip_addr, $ipv6_fallback = false ) { 6142 // Detect what kind of IP address this is. 6143 $ip_prefix = ''; 6144 $is_ipv6 = substr_count( $ip_addr, ':' ) > 1; 6145 $is_ipv4 = ( 3 === substr_count( $ip_addr, '.' ) ); 6146 6147 if ( $is_ipv6 && $is_ipv4 ) { 6148 // IPv6 compatibility mode, temporarily strip the IPv6 part, and treat it like IPv4. 6149 $ip_prefix = '::ffff:'; 6150 $ip_addr = preg_replace( '/^\[?[0-9a-f:]*:/i', '', $ip_addr ); 6151 $ip_addr = str_replace( ']', '', $ip_addr ); 6152 $is_ipv6 = false; 6153 } 6154 6155 if ( $is_ipv6 ) { 6156 // IPv6 addresses will always be enclosed in [] if there's a port. 6157 $left_bracket = strpos( $ip_addr, '[' ); 6158 $right_bracket = strpos( $ip_addr, ']' ); 6159 $percent = strpos( $ip_addr, '%' ); 6160 $netmask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000'; 6161 6162 // Strip the port (and [] from IPv6 addresses), if they exist. 6163 if ( false !== $left_bracket && false !== $right_bracket ) { 6164 $ip_addr = substr( $ip_addr, $left_bracket + 1, $right_bracket - $left_bracket - 1 ); 6165 } elseif ( false !== $left_bracket || false !== $right_bracket ) { 6166 // The IP has one bracket, but not both, so it's malformed. 6167 return '::'; 6168 } 6169 6170 // Strip the reachability scope. 6171 if ( false !== $percent ) { 6172 $ip_addr = substr( $ip_addr, 0, $percent ); 6173 } 6174 6175 // No invalid characters should be left. 6176 if ( preg_match( '/[^0-9a-f:]/i', $ip_addr ) ) { 6177 return '::'; 6178 } 6179 6180 // Partially anonymize the IP by reducing it to the corresponding network ID. 6181 if ( function_exists( 'inet_pton' ) && function_exists( 'inet_ntop' ) ) { 6182 $ip_addr = inet_ntop( inet_pton( $ip_addr ) & inet_pton( $netmask ) ); 6183 } elseif ( ! $ipv6_fallback ) { 6184 return '::'; 6185 } 6186 } elseif ( $is_ipv4 ) { 6187 // Strip any port and partially anonymize the IP. 6188 $last_octet_position = strrpos( $ip_addr, '.' ); 6189 $ip_addr = substr( $ip_addr, 0, $last_octet_position ) . '.0'; 6190 } else { 6191 return '0.0.0.0'; 6192 } 6193 6194 // Restore the IPv6 prefix to compatibility mode addresses. 6195 return $ip_prefix . $ip_addr; 6196 } 6197 6198 /** 6199 * Return uniform "anonymous" data by type. 6200 * 6201 * @since 5.0.0 6202 * 6203 * @param string $type The type of data to be anonymized. 6204 * @param string $data Optional The data to be anonymized. 6205 * @return string The anonymous data for the requested type. 6206 */ 6207 function wp_privacy_anonymize_data( $type, $data = '' ) { 6208 6209 switch ( $type ) { 6210 case 'email': 6211 $anonymous = 'deleted@site.invalid'; 6212 break; 6213 case 'url': 6214 $anonymous = 'https://site.invalid'; 6215 break; 6216 case 'ip': 6217 $anonymous = wp_privacy_anonymize_ip( $data ); 6218 break; 6219 case 'date': 6220 $anonymous = '0000-00-00 00:00:00'; 6221 break; 6222 case 'text': 6223 /* translators: deleted text */ 6224 $anonymous = __( '[deleted]' ); 6225 break; 6226 case 'longtext': 6227 /* translators: deleted long text */ 6228 $anonymous = __( 'This content was deleted by the author.' ); 6229 break; 6230 default: 6231 $anonymous = ''; 6232 } 6233 6234 /** 6235 * Filters the anonymous data for each type. 6236 * 6237 * @since 5.0.0 6238 * 6239 * @param string $anonymous Anonymized data. 6240 * @param string $type Type of the data. 6241 * @param string $data Original data. 6242 */ 6243 return apply_filters( 'wp_privacy_anonymize_data', $anonymous, $type, $data ); 6244 } -
tests/phpunit/tests/admin/includesCommunityEvents.php
diff --git tests/phpunit/tests/admin/includesCommunityEvents.php tests/phpunit/tests/admin/includesCommunityEvents.php index bd6a6cdbe6..0de7f0ab06 100644
class Test_WP_Community_Events extends WP_UnitTestCase { 479 479 /** 480 480 * Test that get_unsafe_client_ip() properly anonymizes all possible address formats 481 481 * 482 * @dataProvider data_get_unsafe_client_ip _anonymization482 * @dataProvider data_get_unsafe_client_ip 483 483 * 484 484 * @ticket 41083 485 485 */ 486 public function test_get_unsafe_client_ip_anonymization( $raw_ip, $expected_result ) { 487 $_SERVER['REMOTE_ADDR'] = $raw_ip; 488 $actual_result = WP_Community_Events::get_unsafe_client_ip(); 486 public function test_get_unsafe_client_ip( $raw_ip, $expected_result ) { 487 $_SERVER['REMOTE_ADDR'] = 'this should not be used'; 488 $_SERVER['HTTP_CLIENT_IP'] = $raw_ip; 489 $actual_result = WP_Community_Events::get_unsafe_client_ip(); 489 490 490 491 $this->assertEquals( $expected_result, $actual_result ); 491 492 } 492 493 493 public function data_get_unsafe_client_ip_anonymization() { 494 /** 495 * Provide test cases for `test_get_unsafe_client_ip()`. 496 * 497 * @return array 498 */ 499 public function data_get_unsafe_client_ip() { 494 500 return array( 495 // Invalid IP. 496 array( 497 '', // Raw IP address 498 false, // Expected result 499 ), 500 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 501 array( 502 'unknown', 503 false, 504 ), 505 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 501 // Handle '::' returned from `wp_privacy_anonymize_ip()`. 506 502 array( 507 503 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001', 508 504 false, 509 505 ), 510 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 511 array( 512 'or=\"1000:0000:0000:0000:0000:0000:0000:0001', 513 false, 514 ), 515 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 506 507 // Handle '0.0.0.0' returned from `wp_privacy_anonymize_ip()`. 516 508 array( 517 ' 1000:0000:0000:0000:0000:0000:0000:0001or=\"',509 'unknown', 518 510 false, 519 511 ), 520 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 521 array( 522 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]:400', 523 '1000::', 524 ), 525 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 526 array( 527 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]', 528 '1000::', 529 ), 530 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 531 array( 532 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]400', 533 '1000::', 534 ), 535 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 536 array( 537 '[1000:0000:0000:0000:0000:0000:0000:0001]:235\"or=', 538 '1000::', 539 ), 540 // IPv4, no port 541 array( 542 '10.20.30.45', 543 '10.20.30.0', 544 ), 545 // IPv4, port 512 513 // Valid IPv4. 546 514 array( 547 '1 0.20.30.45:20000',548 '1 0.20.30.0',515 '198.143.164.252', 516 '198.143.164.0', 549 517 ), 550 // IPv6, no port 518 519 // Valid IPv6. 551 520 array( 552 521 '2a03:2880:2110:df07:face:b00c::1', 553 522 '2a03:2880:2110:df07::', 554 523 ), 555 // IPv6, port556 array(557 '[2a03:2880:2110:df07:face:b00c::1]:20000',558 '2a03:2880:2110:df07::',559 ),560 // IPv6, no port, reducible representation561 array(562 '0000:0000:0000:0000:0000:0000:0000:0001',563 '::',564 ),565 // IPv6, no port, partially reducible representation566 array(567 '1000:0000:0000:0000:0000:0000:0000:0001',568 '1000::',569 ),570 // IPv6, port, reducible representation571 array(572 '[0000:0000:0000:0000:0000:0000:0000:0001]:1234',573 '::',574 ),575 // IPv6, port, partially reducible representation576 array(577 '[1000:0000:0000:0000:0000:0000:0000:0001]:5678',578 '1000::',579 ),580 // IPv6, no port, reduced representation581 array(582 '::',583 '::',584 ),585 // IPv6, no port, reduced representation586 array(587 '::1',588 '::',589 ),590 // IPv6, port, reduced representation591 array(592 '[::]:20000',593 '::',594 ),595 // IPv6, address brackets without port delimiter and number, reduced representation596 array(597 '[::1]',598 '::',599 ),600 // IPv6, no port, compatibility mode601 array(602 '::ffff:10.15.20.25',603 '::ffff:10.15.20.0',604 ),605 // IPv6, port, compatibility mode606 array(607 '[::FFFF:10.15.20.25]:30000',608 '::ffff:10.15.20.0',609 ),610 // IPv6, no port, compatibility mode shorthand611 array(612 '::127.0.0.1',613 '::ffff:127.0.0.0',614 ),615 // IPv6, port, compatibility mode shorthand616 array(617 '[::127.0.0.1]:30000',618 '::ffff:127.0.0.0',619 ),620 // IPv6 with reachability scope621 array(622 'fe80::b059:65f4:e877:c40%16',623 'fe80::',624 ),625 // IPv6 with reachability scope626 array(627 'FE80::B059:65F4:E877:C40%eth0',628 'fe80::',629 ),630 524 ); 631 525 } 632 526 } -
new file tests/phpunit/tests/functions/anonymization.php
diff --git tests/phpunit/tests/functions/anonymization.php tests/phpunit/tests/functions/anonymization.php new file mode 100644 index 0000000000..08b5fedb63
- + 1 <?php 2 3 /** 4 * @group functions.php 5 * @group privacy 6 */ 7 class Tests_Functions_Anonymization extends WP_UnitTestCase { 8 /** 9 * Test that get_unsafe_client_ip() properly anonymizes all possible address formats. 10 * 11 * @dataProvider data_wp_privacy_anonymize_ip 12 * 13 * @ticket 41083 14 * @ticket 43545 15 */ 16 public function test_wp_privacy_anonymize_ip( $raw_ip, $expected_result ) { 17 $actual_result = wp_privacy_anonymize_data( 'ip', $raw_ip ); 18 19 // todo test ipv6_fallback mode if keeping it 20 21 $this->assertEquals( $expected_result, $actual_result ); 22 } 23 24 /** 25 * Provide test cases for `test_wp_privacy_anonymize_ip()`. 26 * 27 * @since 5.0.0 Moved from `Test_WP_Community_Events::data_get_unsafe_client_ip_anonymization()`. 28 * 29 * @return array 30 */ 31 public function data_wp_privacy_anonymize_ip() { 32 return array( 33 // Invalid IP. 34 array( 35 '', 36 '0.0.0.0', 37 ), 38 array( 39 '0.0.0.0.0', 40 '0.0.0.0', 41 ), 42 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 43 array( 44 'unknown', 45 '0.0.0.0', 46 ), 47 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 48 array( 49 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001', 50 '::', 51 ), 52 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 53 array( 54 'or=\"1000:0000:0000:0000:0000:0000:0000:0001', 55 '::', 56 ), 57 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 58 array( 59 '1000:0000:0000:0000:0000:0000:0000:0001or=\"', 60 '::', 61 ), 62 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 63 array( 64 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]:400', 65 '1000::', 66 ), 67 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 68 array( 69 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]', 70 '1000::', 71 ), 72 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 73 array( 74 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]400', 75 '1000::', 76 ), 77 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 78 array( 79 '[1000:0000:0000:0000:0000:0000:0000:0001]:235\"or=', 80 '1000::', 81 ), 82 // IPv4, no port 83 array( 84 '10.20.30.45', 85 '10.20.30.0', 86 ), 87 // IPv4, port 88 array( 89 '10.20.30.45:20000', 90 '10.20.30.0', 91 ), 92 // IPv6, no port 93 array( 94 '2a03:2880:2110:df07:face:b00c::1', 95 '2a03:2880:2110:df07::', 96 ), 97 // IPv6, port 98 array( 99 '[2a03:2880:2110:df07:face:b00c::1]:20000', 100 '2a03:2880:2110:df07::', 101 ), 102 // IPv6, no port, reducible representation 103 array( 104 '0000:0000:0000:0000:0000:0000:0000:0001', 105 '::', 106 ), 107 // IPv6, no port, partially reducible representation 108 array( 109 '1000:0000:0000:0000:0000:0000:0000:0001', 110 '1000::', 111 ), 112 // IPv6, port, reducible representation 113 array( 114 '[0000:0000:0000:0000:0000:0000:0000:0001]:1234', 115 '::', 116 ), 117 // IPv6, port, partially reducible representation 118 array( 119 '[1000:0000:0000:0000:0000:0000:0000:0001]:5678', 120 '1000::', 121 ), 122 // IPv6, no port, reduced representation 123 array( 124 '::', 125 '::', 126 ), 127 // IPv6, no port, reduced representation 128 array( 129 '::1', 130 '::', 131 ), 132 // IPv6, port, reduced representation 133 array( 134 '[::]:20000', 135 '::', 136 ), 137 // IPv6, address brackets without port delimiter and number, reduced representation 138 array( 139 '[::1]', 140 '::', 141 ), 142 // IPv6, no port, compatibility mode 143 array( 144 '::ffff:10.15.20.25', 145 '::ffff:10.15.20.0', 146 ), 147 // IPv6, port, compatibility mode 148 array( 149 '[::FFFF:10.15.20.25]:30000', 150 '::ffff:10.15.20.0', 151 ), 152 // IPv6, no port, compatibility mode shorthand 153 array( 154 '::127.0.0.1', 155 '::ffff:127.0.0.0', 156 ), 157 // IPv6, port, compatibility mode shorthand 158 array( 159 '[::127.0.0.1]:30000', 160 '::ffff:127.0.0.0', 161 ), 162 // IPv6 with reachability scope 163 array( 164 'fe80::b059:65f4:e877:c40%16', 165 'fe80::', 166 ), 167 // IPv6 with reachability scope 168 array( 169 'FE80::B059:65F4:E877:C40%eth0', 170 'fe80::', 171 ), 172 ); 173 } 174 175 function test_anonymize_email() { 176 $this->assertEquals( 'deleted@site.invalid', wp_privacy_anonymize_data( 'email', 'bar@example.com' ) ); 177 } 178 179 function test_anonymize_url() { 180 $this->assertEquals( 'https://site.invalid', wp_privacy_anonymize_data( 'url', 'https://example.com/author/username' ) ); 181 } 182 183 function test_anonymize_date() { 184 $this->assertEquals( '0000-00-00 00:00:00', wp_privacy_anonymize_data( 'date', '2003-12-25 12:34:56' ) ); 185 } 186 187 function test_anonymize_text() { 188 $text = __( 'Four score and seven years ago' ); 189 $this->assertNotEquals( $text, wp_privacy_anonymize_data( 'text', $text ) ); 190 } 191 192 function test_anonymize_long_text() { 193 $text = __( 'Four score and seven years ago' ); 194 $this->assertNotEquals( $text, wp_privacy_anonymize_data( 'longtext', $text ) ); 195 } 196 }