Changeset 3321915
- Timestamp:
- 07/03/2025 04:48:42 PM (8 months ago)
- Location:
- zapier
- Files:
-
- 2 edited
- 1 copied
-
tags/1.5.3 (copied) (copied from zapier/trunk)
-
tags/1.5.3/zapier.php (modified) (14 diffs)
-
trunk/zapier.php (modified) (14 diffs)
Legend:
- Unmodified
- Added
- Removed
-
zapier/tags/1.5.3/zapier.php
r3257975 r3321915 4 4 * Plugin Name: Zapier for WordPress 5 5 * Description: Zapier enables you to automatically share your posts to social media, create WordPress posts from Mailchimp newsletters, and much more. Visit https://zapier.com/apps/wordpress/integrations for more details. 6 * Version: 1.5. 26 * Version: 1.5.3 7 7 * Author: Zapier 8 8 * Author URI: https://zapier.com … … 92 92 } 93 93 94 /** 95 * Verify user has proper authorization for webhook management 96 * 97 * @param WP_REST_Request $request 98 * @return bool|WP_Error 99 */ 100 private function verify_webhook_authorization($request) 101 { 102 // Check if user is logged in 103 if (!is_user_logged_in()) { 104 return new WP_Error( 105 'not_logged_in', 106 'You are not logged in', 107 array('status' => 401) 108 ); 109 } 110 111 // Check if user has proper capabilities - only administrators should manage webhooks 112 if (!current_user_can('manage_options')) { 113 return new WP_Error( 114 'insufficient_permissions', 115 'You do not have sufficient permissions to manage webhooks', 116 array('status' => 403) 117 ); 118 } 119 120 // CSRF protection: Different approaches for different auth methods 121 $is_jwt_auth = isset($_SERVER['HTTP_X_ZAPIER_AUTH']) && isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] === 'Zapier'; 122 123 if ($is_jwt_auth) { 124 // For JWT: Verify the token includes proper origin validation 125 // JWT tokens should only be used by Zapier's servers, not browsers 126 // The User-Agent check provides additional CSRF protection 127 if (!isset($_SERVER['HTTP_USER_AGENT']) || $_SERVER['HTTP_USER_AGENT'] !== 'Zapier') { 128 return new WP_Error( 129 'invalid_user_agent', 130 'Invalid request source', 131 array('status' => 403) 132 ); 133 } 134 } else { 135 // For browser-based requests: Use WordPress nonce 136 $nonce = $request->get_header('X-WP-Nonce'); 137 if (!$nonce || !wp_verify_nonce($nonce, 'wp_rest')) { 138 return new WP_Error( 139 'invalid_nonce', 140 'Invalid security token', 141 array('status' => 403) 142 ); 143 } 144 } 145 146 return true; 147 } 148 149 /** 150 * Verify user authorization for read-only operations 151 * 152 * @param WP_REST_Request $request 153 * @return bool|WP_Error 154 */ 155 private function verify_read_authorization($request) 156 { 157 // Check if user is logged in 158 if (!is_user_logged_in()) { 159 return new WP_Error( 160 'not_logged_in', 161 'You are not logged in', 162 array('status' => 401) 163 ); 164 } 165 166 // For read operations, we can allow users with edit_posts capability 167 if (!current_user_can('edit_posts')) { 168 return new WP_Error( 169 'insufficient_permissions', 170 'You do not have sufficient permissions to access this resource', 171 array('status' => 403) 172 ); 173 } 174 175 return true; 176 } 177 178 179 94 180 public function add_api_routes() 95 181 { … … 103 189 'methods' => "GET", 104 190 'callback' => array($this, 'get_custom_type_supports'), 105 'permission_callback' => '__return_true'191 'permission_callback' => array($this, 'check_read_permission') 106 192 )); 107 193 … … 109 195 'methods' => "GET", 110 196 'callback' => array($this, 'get_roles'), 111 'permission_callback' => '__return_true'197 'permission_callback' => array($this, 'check_read_permission') 112 198 )); 113 199 … … 115 201 'methods' => "POST", 116 202 'callback' => array($this, 'add_webhook'), 117 'permission_callback' => '__return_true'203 'permission_callback' => array($this, 'check_webhook_permission') 118 204 )); 119 205 … … 121 207 'methods' => "DELETE", 122 208 'callback' => array($this, 'remove_webhook'), 123 'permission_callback' => '__return_true'209 'permission_callback' => array($this, 'check_webhook_permission') 124 210 )); 211 } 212 213 /** 214 * Permission callback for webhook operations 215 */ 216 public function check_webhook_permission($request) 217 { 218 $auth_result = $this->verify_webhook_authorization($request); 219 return !is_wp_error($auth_result); 220 } 221 222 /** 223 * Permission callback for read operations 224 */ 225 public function check_read_permission($request) 226 { 227 // Check if user is logged in (Application Password authentication should work here) 228 if (!is_user_logged_in()) { 229 return false; 230 } 231 232 // For read operations, allow users with edit_posts capability (editors and admins) 233 if (!current_user_can('edit_posts')) { 234 return false; 235 } 236 237 return true; 125 238 } 126 239 … … 161 274 public function get_custom_type_supports($request) 162 275 { 163 164 if(!is_user_logged_in()) { 165 return new WP_Error( 166 'not_logged_in', 167 'You are not logged in', 168 array( 169 'status' => 401, 170 ) 171 ); 172 } 173 276 // Authorization is handled by permission_callback 174 277 $type = $request['type']; 175 278 $types = get_post_types(array()); … … 184 287 ); 185 288 } 186 289 187 290 return array('supports' => get_all_post_type_supports($type)); 188 291 } 189 292 190 public function get_roles() 191 { 192 if(!is_user_logged_in()) { 193 return new WP_Error( 194 'not_logged_in', 195 'You are not logged in', 196 array( 197 'status' => 401, 198 ) 199 ); 200 } 201 293 public function get_roles($request = null) 294 { 295 // Authorization is handled by permission_callback 202 296 $roles = array(); 203 297 foreach (wp_roles()->roles as $key => $role) { … … 209 303 210 304 public function add_webhook($request) { 211 212 if(!is_user_logged_in()) { 213 return new WP_Error( 214 'not_logged_in', 215 'You are not logged in', 216 array( 217 'status' => 401, 218 ) 219 ); 305 // Authorization is handled by permission_callback 306 $auth_result = $this->verify_webhook_authorization($request); 307 if (is_wp_error($auth_result)) { 308 return $auth_result; 220 309 } 221 310 … … 245 334 } 246 335 336 // Enhanced URL validation 337 if (!$this->is_safe_url($endpoint_url)) { 338 return new WP_Error( 339 'unsafe_endpoint_url', 340 'The provided endpoint URL is not allowed for security reasons', 341 array( 342 'status' => 400, 343 ) 344 ); 345 } 346 247 347 $option_key = "zapier_hooks_$action"; 248 348 … … 258 358 259 359 public function remove_webhook($request) { 260 261 if(!is_user_logged_in()) { 262 return new WP_Error( 263 'not_logged_in', 264 'You are not logged in', 265 array( 266 'status' => 401, 267 ) 268 ); 360 // Authorization is handled by permission_callback 361 $auth_result = $this->verify_webhook_authorization($request); 362 if (is_wp_error($auth_result)) { 363 return $auth_result; 269 364 } 270 365 … … 286 381 $option_key = "zapier_hooks_wp_update_user"; 287 382 $hooks = get_option($option_key, []); 288 383 289 384 foreach ($hooks as $hook) { 290 385 // Validate the URL … … 292 387 continue; // Skip unsafe URLs 293 388 } 294 389 295 390 // Use wp_safe_remote_post to ensure secure requests 296 391 $response = wp_safe_remote_post($hook, [ … … 303 398 public function updated_post($post_id, $post_after, $post_before) { 304 399 $option_key = "zapier_hooks_post_updated"; 305 400 306 401 $rest_base = get_post_type_object($post_after->post_type)->rest_base; 307 402 $changed_properties = $this->compareObjects($post_after, $post_before); 308 403 309 404 $hooks = get_option($option_key, []); 310 405 -
zapier/trunk/zapier.php
r3257975 r3321915 4 4 * Plugin Name: Zapier for WordPress 5 5 * Description: Zapier enables you to automatically share your posts to social media, create WordPress posts from Mailchimp newsletters, and much more. Visit https://zapier.com/apps/wordpress/integrations for more details. 6 * Version: 1.5. 26 * Version: 1.5.3 7 7 * Author: Zapier 8 8 * Author URI: https://zapier.com … … 92 92 } 93 93 94 /** 95 * Verify user has proper authorization for webhook management 96 * 97 * @param WP_REST_Request $request 98 * @return bool|WP_Error 99 */ 100 private function verify_webhook_authorization($request) 101 { 102 // Check if user is logged in 103 if (!is_user_logged_in()) { 104 return new WP_Error( 105 'not_logged_in', 106 'You are not logged in', 107 array('status' => 401) 108 ); 109 } 110 111 // Check if user has proper capabilities - only administrators should manage webhooks 112 if (!current_user_can('manage_options')) { 113 return new WP_Error( 114 'insufficient_permissions', 115 'You do not have sufficient permissions to manage webhooks', 116 array('status' => 403) 117 ); 118 } 119 120 // CSRF protection: Different approaches for different auth methods 121 $is_jwt_auth = isset($_SERVER['HTTP_X_ZAPIER_AUTH']) && isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] === 'Zapier'; 122 123 if ($is_jwt_auth) { 124 // For JWT: Verify the token includes proper origin validation 125 // JWT tokens should only be used by Zapier's servers, not browsers 126 // The User-Agent check provides additional CSRF protection 127 if (!isset($_SERVER['HTTP_USER_AGENT']) || $_SERVER['HTTP_USER_AGENT'] !== 'Zapier') { 128 return new WP_Error( 129 'invalid_user_agent', 130 'Invalid request source', 131 array('status' => 403) 132 ); 133 } 134 } else { 135 // For browser-based requests: Use WordPress nonce 136 $nonce = $request->get_header('X-WP-Nonce'); 137 if (!$nonce || !wp_verify_nonce($nonce, 'wp_rest')) { 138 return new WP_Error( 139 'invalid_nonce', 140 'Invalid security token', 141 array('status' => 403) 142 ); 143 } 144 } 145 146 return true; 147 } 148 149 /** 150 * Verify user authorization for read-only operations 151 * 152 * @param WP_REST_Request $request 153 * @return bool|WP_Error 154 */ 155 private function verify_read_authorization($request) 156 { 157 // Check if user is logged in 158 if (!is_user_logged_in()) { 159 return new WP_Error( 160 'not_logged_in', 161 'You are not logged in', 162 array('status' => 401) 163 ); 164 } 165 166 // For read operations, we can allow users with edit_posts capability 167 if (!current_user_can('edit_posts')) { 168 return new WP_Error( 169 'insufficient_permissions', 170 'You do not have sufficient permissions to access this resource', 171 array('status' => 403) 172 ); 173 } 174 175 return true; 176 } 177 178 179 94 180 public function add_api_routes() 95 181 { … … 103 189 'methods' => "GET", 104 190 'callback' => array($this, 'get_custom_type_supports'), 105 'permission_callback' => '__return_true'191 'permission_callback' => array($this, 'check_read_permission') 106 192 )); 107 193 … … 109 195 'methods' => "GET", 110 196 'callback' => array($this, 'get_roles'), 111 'permission_callback' => '__return_true'197 'permission_callback' => array($this, 'check_read_permission') 112 198 )); 113 199 … … 115 201 'methods' => "POST", 116 202 'callback' => array($this, 'add_webhook'), 117 'permission_callback' => '__return_true'203 'permission_callback' => array($this, 'check_webhook_permission') 118 204 )); 119 205 … … 121 207 'methods' => "DELETE", 122 208 'callback' => array($this, 'remove_webhook'), 123 'permission_callback' => '__return_true'209 'permission_callback' => array($this, 'check_webhook_permission') 124 210 )); 211 } 212 213 /** 214 * Permission callback for webhook operations 215 */ 216 public function check_webhook_permission($request) 217 { 218 $auth_result = $this->verify_webhook_authorization($request); 219 return !is_wp_error($auth_result); 220 } 221 222 /** 223 * Permission callback for read operations 224 */ 225 public function check_read_permission($request) 226 { 227 // Check if user is logged in (Application Password authentication should work here) 228 if (!is_user_logged_in()) { 229 return false; 230 } 231 232 // For read operations, allow users with edit_posts capability (editors and admins) 233 if (!current_user_can('edit_posts')) { 234 return false; 235 } 236 237 return true; 125 238 } 126 239 … … 161 274 public function get_custom_type_supports($request) 162 275 { 163 164 if(!is_user_logged_in()) { 165 return new WP_Error( 166 'not_logged_in', 167 'You are not logged in', 168 array( 169 'status' => 401, 170 ) 171 ); 172 } 173 276 // Authorization is handled by permission_callback 174 277 $type = $request['type']; 175 278 $types = get_post_types(array()); … … 184 287 ); 185 288 } 186 289 187 290 return array('supports' => get_all_post_type_supports($type)); 188 291 } 189 292 190 public function get_roles() 191 { 192 if(!is_user_logged_in()) { 193 return new WP_Error( 194 'not_logged_in', 195 'You are not logged in', 196 array( 197 'status' => 401, 198 ) 199 ); 200 } 201 293 public function get_roles($request = null) 294 { 295 // Authorization is handled by permission_callback 202 296 $roles = array(); 203 297 foreach (wp_roles()->roles as $key => $role) { … … 209 303 210 304 public function add_webhook($request) { 211 212 if(!is_user_logged_in()) { 213 return new WP_Error( 214 'not_logged_in', 215 'You are not logged in', 216 array( 217 'status' => 401, 218 ) 219 ); 305 // Authorization is handled by permission_callback 306 $auth_result = $this->verify_webhook_authorization($request); 307 if (is_wp_error($auth_result)) { 308 return $auth_result; 220 309 } 221 310 … … 245 334 } 246 335 336 // Enhanced URL validation 337 if (!$this->is_safe_url($endpoint_url)) { 338 return new WP_Error( 339 'unsafe_endpoint_url', 340 'The provided endpoint URL is not allowed for security reasons', 341 array( 342 'status' => 400, 343 ) 344 ); 345 } 346 247 347 $option_key = "zapier_hooks_$action"; 248 348 … … 258 358 259 359 public function remove_webhook($request) { 260 261 if(!is_user_logged_in()) { 262 return new WP_Error( 263 'not_logged_in', 264 'You are not logged in', 265 array( 266 'status' => 401, 267 ) 268 ); 360 // Authorization is handled by permission_callback 361 $auth_result = $this->verify_webhook_authorization($request); 362 if (is_wp_error($auth_result)) { 363 return $auth_result; 269 364 } 270 365 … … 286 381 $option_key = "zapier_hooks_wp_update_user"; 287 382 $hooks = get_option($option_key, []); 288 383 289 384 foreach ($hooks as $hook) { 290 385 // Validate the URL … … 292 387 continue; // Skip unsafe URLs 293 388 } 294 389 295 390 // Use wp_safe_remote_post to ensure secure requests 296 391 $response = wp_safe_remote_post($hook, [ … … 303 398 public function updated_post($post_id, $post_after, $post_before) { 304 399 $option_key = "zapier_hooks_post_updated"; 305 400 306 401 $rest_base = get_post_type_object($post_after->post_type)->rest_base; 307 402 $changed_properties = $this->compareObjects($post_after, $post_before); 308 403 309 404 $hooks = get_option($option_key, []); 310 405
Note: See TracChangeset
for help on using the changeset viewer.