Automattic /
jetpack
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * These functions are shared by the Protect module and its related json-endpoints |
||
| 4 | */ |
||
| 5 | /** |
||
| 6 | * Returns an array of IP objects that will never be blocked by the Protect module |
||
| 7 | * |
||
| 8 | * The array is segmented into a local whitelist which applies only to the current site |
||
| 9 | * and a global whitelist which, for multisite installs, applies to the entire networko |
||
| 10 | * |
||
| 11 | * @return array |
||
| 12 | */ |
||
| 13 | function jetpack_protect_format_whitelist() { |
||
| 14 | $local_whitelist = jetpack_protect_get_local_whitelist(); |
||
| 15 | $formatted = array( |
||
| 16 | 'local' => array(), |
||
| 17 | ); |
||
| 18 | View Code Duplication | foreach ( $local_whitelist as $item ) { |
|
| 19 | if ( $item->range ) { |
||
| 20 | $formatted['local'][] = $item->range_low . ' - ' . $item->range_high; |
||
| 21 | } else { |
||
| 22 | $formatted['local'][] = $item->ip_address; |
||
| 23 | } |
||
| 24 | } |
||
| 25 | if ( is_multisite() && current_user_can( 'manage_network' ) ) { |
||
| 26 | $formatted['global'] = array(); |
||
| 27 | $global_whitelist = jetpack_protect_get_global_whitelist(); |
||
| 28 | if ( false === $global_whitelist ) { |
||
| 29 | // If the global whitelist has never been set, check for a legacy option set prior to 3.6. |
||
| 30 | $global_whitelist = get_site_option( 'jetpack_protect_whitelist', array() ); |
||
| 31 | } |
||
| 32 | View Code Duplication | foreach ( $global_whitelist as $item ) { |
|
| 33 | if ( $item->range ) { |
||
| 34 | $formatted['global'][] = $item->range_low . ' - ' . $item->range_high; |
||
| 35 | } else { |
||
| 36 | $formatted['global'][] = $item->ip_address; |
||
| 37 | } |
||
| 38 | } |
||
| 39 | } |
||
| 40 | return $formatted; |
||
| 41 | } |
||
| 42 | /** |
||
| 43 | * Gets the local Protect whitelist |
||
| 44 | * |
||
| 45 | * The 'local' part of the whitelist only really applies to multisite installs, |
||
| 46 | * which can have a network wide whitelist, as well as a local list that applies |
||
| 47 | * only to the current site. On single site installs, there will only be a local |
||
| 48 | * whitelist. |
||
| 49 | * |
||
| 50 | * @return array A list of IP Address objects or an empty array |
||
| 51 | */ |
||
| 52 | function jetpack_protect_get_local_whitelist() { |
||
| 53 | $whitelist = Jetpack_Options::get_option( 'protect_whitelist' ); |
||
| 54 | if ( false === $whitelist ) { |
||
| 55 | // The local whitelist has never been set. |
||
| 56 | if ( is_multisite() ) { |
||
| 57 | // On a multisite, we can check for a legacy site_option that existed prior to v 3.6, or default to an empty array. |
||
| 58 | $whitelist = get_site_option( 'jetpack_protect_whitelist', array() ); |
||
| 59 | } else { |
||
| 60 | // On a single site, we can just use an empty array. |
||
| 61 | $whitelist = array(); |
||
| 62 | } |
||
| 63 | } |
||
| 64 | return $whitelist; |
||
| 65 | } |
||
| 66 | |||
| 67 | /** |
||
| 68 | * Get the global, network-wide whitelist |
||
| 69 | * |
||
| 70 | * It will revert to the legacy site_option if jetpack_protect_global_whitelist has never been set. |
||
| 71 | * |
||
| 72 | * @return array |
||
| 73 | */ |
||
| 74 | function jetpack_protect_get_global_whitelist() { |
||
| 75 | $whitelist = get_site_option( 'jetpack_protect_global_whitelist' ); |
||
| 76 | if ( false === $whitelist ) { |
||
| 77 | // The global whitelist has never been set. Check for legacy site_option, or default to an empty array. |
||
| 78 | $whitelist = get_site_option( 'jetpack_protect_whitelist', array() ); |
||
| 79 | } |
||
| 80 | return $whitelist; |
||
| 81 | } |
||
| 82 | |||
| 83 | /** |
||
| 84 | * Jetpack Protect Save Whitelist. |
||
| 85 | * |
||
| 86 | * @access public |
||
| 87 | * @param mixed $whitelist Whitelist. |
||
| 88 | * @param bool $global (default: false) Global. |
||
| 89 | * @return Bool. |
||
| 90 | */ |
||
| 91 | function jetpack_protect_save_whitelist( $whitelist, $global = false ) { |
||
| 92 | $whitelist_error = false; |
||
| 93 | $new_items = array(); |
||
| 94 | if ( ! is_array( $whitelist ) ) { |
||
| 95 | return new WP_Error( 'invalid_parameters', __( 'Expecting an array', 'jetpack' ) ); |
||
|
0 ignored issues
–
show
|
|||
| 96 | } |
||
| 97 | if ( $global && ! is_multisite() ) { |
||
| 98 | return new WP_Error( 'invalid_parameters', __( 'Cannot use global flag on non-multisites', 'jetpack' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_parameters'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 99 | } |
||
| 100 | if ( $global && ! current_user_can( 'manage_network' ) ) { |
||
| 101 | return new WP_Error( 'permission_denied', __( 'Only super admins can edit the global whitelist', 'jetpack' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'permission_denied'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 102 | } |
||
| 103 | // Validate each item. |
||
| 104 | foreach ( $whitelist as $item ) { |
||
| 105 | $item = trim( $item ); |
||
| 106 | if ( empty( $item ) ) { |
||
| 107 | continue; |
||
| 108 | } |
||
| 109 | $range = false; |
||
| 110 | if ( strpos( $item, '-' ) ) { |
||
| 111 | $item = explode( '-', $item ); |
||
| 112 | $range = true; |
||
| 113 | } |
||
| 114 | $new_item = new stdClass(); |
||
| 115 | $new_item->range = $range; |
||
| 116 | if ( ! empty( $range ) ) { |
||
| 117 | $low = trim( $item[0] ); |
||
| 118 | $high = trim( $item[1] ); |
||
| 119 | if ( ! filter_var( $low, FILTER_VALIDATE_IP ) || ! filter_var( $high, FILTER_VALIDATE_IP ) ) { |
||
| 120 | $whitelist_error = true; |
||
| 121 | break; |
||
| 122 | } |
||
| 123 | if ( ! jetpack_convert_ip_address( $low ) || ! jetpack_convert_ip_address( $high ) ) { |
||
| 124 | $whitelist_error = true; |
||
| 125 | break; |
||
| 126 | } |
||
| 127 | $new_item->range_low = $low; |
||
| 128 | $new_item->range_high = $high; |
||
| 129 | } else { |
||
| 130 | if ( ! filter_var( $item, FILTER_VALIDATE_IP ) ) { |
||
| 131 | $whitelist_error = true; |
||
| 132 | break; |
||
| 133 | } |
||
| 134 | if ( ! jetpack_convert_ip_address( $item ) ) { |
||
| 135 | $whitelist_error = true; |
||
| 136 | break; |
||
| 137 | } |
||
| 138 | $new_item->ip_address = $item; |
||
| 139 | } |
||
| 140 | $new_items[] = $new_item; |
||
| 141 | } // End item loop. |
||
| 142 | if ( ! empty( $whitelist_error ) ) { |
||
| 143 | return new WP_Error( 'invalid_ip', __( 'One of your IP addresses was not valid.', 'jetpack' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_ip'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 144 | } |
||
| 145 | if ( $global ) { |
||
| 146 | update_site_option( 'jetpack_protect_global_whitelist', $new_items ); |
||
| 147 | // Once a user has saved their global whitelist, we can permanently remove the legacy option. |
||
| 148 | delete_site_option( 'jetpack_protect_whitelist' ); |
||
| 149 | } else { |
||
| 150 | Jetpack_Options::update_option( 'protect_whitelist', $new_items ); |
||
| 151 | } |
||
| 152 | return true; |
||
| 153 | } |
||
| 154 | |||
| 155 | /** |
||
| 156 | * Jetpack Protect Get IP. |
||
| 157 | * |
||
| 158 | * @access public |
||
| 159 | * @return IP. |
||
| 160 | */ |
||
| 161 | function jetpack_protect_get_ip() { |
||
| 162 | $trusted_header_data = get_site_option( 'trusted_ip_header' ); |
||
| 163 | if ( isset( $trusted_header_data->trusted_header ) && isset( $_SERVER[ $trusted_header_data->trusted_header ] ) ) { |
||
| 164 | $ip = $_SERVER[ $trusted_header_data->trusted_header ]; |
||
| 165 | $segments = $trusted_header_data->segments; |
||
| 166 | $reverse_order = $trusted_header_data->reverse; |
||
| 167 | } else { |
||
| 168 | $ip = $_SERVER['REMOTE_ADDR']; |
||
| 169 | } |
||
| 170 | |||
| 171 | if ( ! $ip ) { |
||
| 172 | return false; |
||
| 173 | } |
||
| 174 | |||
| 175 | |||
| 176 | |||
| 177 | $ips = explode( ',', $ip ); |
||
| 178 | if ( ! isset( $segments ) || ! $segments ) { |
||
| 179 | $segments = 1; |
||
| 180 | } |
||
| 181 | if ( isset( $reverse_order ) && $reverse_order ) { |
||
| 182 | $ips = array_reverse( $ips ); |
||
| 183 | } |
||
| 184 | $ip_count = count( $ips ); |
||
| 185 | if ( 1 === $ip_count ) { |
||
| 186 | return jetpack_clean_ip( $ips[0] ); |
||
| 187 | } elseif ( $ip_count >= $segments ) { |
||
| 188 | $the_one = $ip_count - $segments; |
||
| 189 | return jetpack_clean_ip( $ips[ $the_one ] ); |
||
| 190 | } else { |
||
| 191 | return jetpack_clean_ip( $_SERVER['REMOTE_ADDR'] ); |
||
| 192 | } |
||
| 193 | } |
||
| 194 | |||
| 195 | /** |
||
| 196 | * Jetpack Clean IP. |
||
| 197 | * |
||
| 198 | * @access public |
||
| 199 | * @param mixed $ip IP. |
||
| 200 | * @return $ip IP. |
||
| 201 | */ |
||
| 202 | function jetpack_clean_ip( $ip ) { |
||
| 203 | |||
| 204 | // Some misconfigured servers give back extra info, which comes after "unless" |
||
| 205 | $ips = explode( ' unless ', $ip ); |
||
| 206 | $ip = $ips[0]; |
||
| 207 | |||
| 208 | $ip = trim( $ip ); |
||
| 209 | // Check for IPv4 IP cast as IPv6. |
||
| 210 | if ( preg_match( '/^::ffff:(\d+\.\d+\.\d+\.\d+)$/', $ip, $matches ) ) { |
||
| 211 | $ip = $matches[1]; |
||
| 212 | } |
||
| 213 | |||
| 214 | if ( function_exists( 'parse_url' ) ) { |
||
| 215 | $parsed_url = parse_url( $ip ); |
||
| 216 | |||
| 217 | if ( isset( $parsed_url['host'] ) ) { |
||
| 218 | $ip = $parsed_url['host']; |
||
| 219 | } elseif ( isset( $parsed_url['path'] ) ) { |
||
| 220 | $ip = $parsed_url['path']; |
||
| 221 | } |
||
| 222 | } else { |
||
| 223 | $colon_count = substr_count( $ip, ':' ); |
||
| 224 | if ( 1 == $colon_count ) { |
||
| 225 | $ips = explode( ':', $ip ); |
||
| 226 | $ip = $ips[0]; |
||
| 227 | } |
||
| 228 | } |
||
| 229 | |||
| 230 | return $ip; |
||
| 231 | } |
||
| 232 | |||
| 233 | /** |
||
| 234 | * Checks an IP to see if it is within a private range. |
||
| 235 | * |
||
| 236 | * @param int $ip IP. |
||
| 237 | * @return bool |
||
| 238 | */ |
||
| 239 | function jetpack_protect_ip_is_private( $ip ) { |
||
| 240 | // We are dealing with ipv6, so we can simply rely on filter_var. |
||
| 241 | if ( false === strpos( $ip, '.' ) ) { |
||
| 242 | return ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ); |
||
| 243 | } |
||
| 244 | // We are dealing with ipv4. |
||
| 245 | $private_ip4_addresses = array( |
||
| 246 | '10.0.0.0|10.255.255.255', // Single class A network. |
||
| 247 | '172.16.0.0|172.31.255.255', // 16 contiguous class B network. |
||
| 248 | '192.168.0.0|192.168.255.255', // 256 contiguous class C network. |
||
| 249 | '169.254.0.0|169.254.255.255', // Link-local address also referred to as Automatic Private IP Addressing. |
||
| 250 | '127.0.0.0|127.255.255.255', // localhost. |
||
| 251 | ); |
||
| 252 | $long_ip = ip2long( $ip ); |
||
| 253 | if ( -1 !== $long_ip ) { |
||
| 254 | foreach ( $private_ip4_addresses as $pri_addr ) { |
||
| 255 | list ( $start, $end ) = explode( '|', $pri_addr ); |
||
| 256 | if ( $long_ip >= ip2long( $start ) && $long_ip <= ip2long( $end ) ) { |
||
| 257 | return true; |
||
| 258 | } |
||
| 259 | } |
||
| 260 | } |
||
| 261 | return false; |
||
| 262 | } |
||
| 263 | |||
| 264 | /** |
||
| 265 | * Uses inet_pton if available to convert an IP address to a binary string. |
||
| 266 | * If inet_pton is not available, ip2long will convert the address to an integer. |
||
| 267 | * Returns false if an invalid IP address is given. |
||
| 268 | * |
||
| 269 | * NOTE: ip2long will return false for any ipv6 address. servers that do not support |
||
| 270 | * inet_pton will not support ipv6 |
||
| 271 | * |
||
| 272 | * @access public |
||
| 273 | * @param mixed $ip IP. |
||
| 274 | * @return int|string|bool |
||
| 275 | */ |
||
| 276 | function jetpack_convert_ip_address( $ip ) { |
||
| 277 | if ( function_exists( 'inet_pton' ) ) { |
||
| 278 | return inet_pton( $ip ); |
||
| 279 | } |
||
| 280 | return ip2long( $ip ); |
||
| 281 | } |
||
| 282 | |||
| 283 | /** |
||
| 284 | * Checks that a given IP address is within a given low - high range. |
||
| 285 | * Servers that support inet_pton will use that function to convert the ip to number, |
||
| 286 | * while other servers will use ip2long. |
||
| 287 | * |
||
| 288 | * NOTE: servers that do not support inet_pton cannot support ipv6. |
||
| 289 | * |
||
| 290 | * @access public |
||
| 291 | * @param mixed $ip IP. |
||
| 292 | * @param mixed $range_low Range Low. |
||
| 293 | * @param mixed $range_high Range High. |
||
| 294 | * @return Bool. |
||
| 295 | */ |
||
| 296 | function jetpack_protect_ip_address_is_in_range( $ip, $range_low, $range_high ) { |
||
| 297 | // The inet_pton will give us binary string of an ipv4 or ipv6. |
||
| 298 | // We can then use strcmp to see if the address is in range. |
||
| 299 | if ( function_exists( 'inet_pton' ) ) { |
||
| 300 | $ip_num = inet_pton( $ip ); |
||
| 301 | $ip_low = inet_pton( $range_low ); |
||
| 302 | $ip_high = inet_pton( $range_high ); |
||
| 303 | if ( $ip_num && $ip_low && $ip_high && strcmp( $ip_num, $ip_low ) >= 0 && strcmp( $ip_num, $ip_high ) <= 0 ) { |
||
| 304 | return true; |
||
| 305 | } |
||
| 306 | // The ip2long will give us an integer of an ipv4 address only. it will produce FALSE for ipv6. |
||
| 307 | } else { |
||
| 308 | $ip_num = ip2long( $ip ); |
||
| 309 | $ip_low = ip2long( $range_low ); |
||
| 310 | $ip_high = ip2long( $range_high ); |
||
| 311 | if ( $ip_num && $ip_low && $ip_high && $ip_num >= $ip_low && $ip_num <= $ip_high ) { |
||
| 312 | return true; |
||
| 313 | } |
||
| 314 | } |
||
| 315 | return false; |
||
| 316 | } |
||
| 317 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.