@@ -380,6 +380,12 @@ public function generate_request_token( $params ) {
380
380
);
381
381
$ data = apply_filters ( 'json_oauth1_request_token_data ' , $ data );
382
382
add_option ( 'oauth1_request_ ' . $ key , $ data , null , 'no ' );
383
+ if ( ! empty ( $ params ['oauth_callback ' ] ) ) {
384
+ $ error = $ this ->set_request_token_callback ( $ key , $ params ['oauth_callback ' ] );
385
+ if ( $ error ) {
386
+ return $ error ;
387
+ }
388
+ }
383
389
384
390
$ data = array (
385
391
'oauth_token ' => self ::urlencode_rfc3986 ($ key ),
@@ -395,7 +401,8 @@ public function set_request_token_callback( $key, $callback ) {
395
401
return $ token ;
396
402
}
397
403
398
- if ( esc_url_raw ( $ callback ) !== $ callback ) {
404
+ $ consumer = $ token ['consumer ' ];
405
+ if ( ! $ this ->validate_callback ( $ callback ) || ! $ this ->check_callback ( $ callback , $ consumer ) ) {
399
406
return new WP_Error ( 'json_oauth1_invalid_callback ' , __ ( 'Callback URL is invalid ' ) );
400
407
}
401
408
@@ -404,6 +411,92 @@ public function set_request_token_callback( $key, $callback ) {
404
411
return $ token ['verifier ' ];
405
412
}
406
413
414
+ /**
415
+ * Validate a callback URL.
416
+ *
417
+ * Based on {@see wp_http_validate_url}, but less restrictive around ports
418
+ * and hosts. In particular, it allows any scheme, host or port rather than
419
+ * just HTTP with standard ports.
420
+ *
421
+ * @param string $url URL for the callback.
422
+ * @return bool True for a valid callback URL, false otherwise.
423
+ */
424
+ protected function validate_callback ( $ url ) {
425
+ if ( strpos ( $ url , ': ' ) === false ) {
426
+ return false ;
427
+ }
428
+
429
+ $ parsed_url = wp_parse_url ( $ url );
430
+ if ( ! $ parsed_url || empty ( $ parsed_url ['host ' ] ) )
431
+ return false ;
432
+
433
+ if ( isset ( $ parsed_url ['user ' ] ) || isset ( $ parsed_url ['pass ' ] ) )
434
+ return false ;
435
+
436
+ if ( false !== strpbrk ( $ parsed_url ['host ' ], ':#?[] ' ) )
437
+ return false ;
438
+
439
+ return true ;
440
+ }
441
+
442
+ /**
443
+ * Check whether a callback is valid for a given consumer.
444
+ *
445
+ * @param string $url Supplied callback.
446
+ * @param int|WP_Post $consumer_id Consumer post ID or object.
447
+ * @return bool True if valid, false otherwise.
448
+ */
449
+ public function check_callback ( $ url , $ consumer_id ) {
450
+ $ consumer = get_post ( $ consumer_id );
451
+ if ( empty ( $ consumer ) || $ consumer ->post_type !== 'json_consumer ' || $ consumer ->type !== $ this ->type ) {
452
+ return false ;
453
+ }
454
+
455
+ $ registered = $ consumer ->callback ;
456
+ if ( empty ( $ registered ) ) {
457
+ return false ;
458
+ }
459
+
460
+ $ registered = wp_parse_url ( $ registered );
461
+ $ supplied = wp_parse_url ( $ url );
462
+
463
+ // Check all components except query and fragment
464
+ $ parts = array ( 'scheme ' , 'host ' , 'port ' , 'user ' , 'pass ' , 'path ' );
465
+ $ valid = true ;
466
+ foreach ( $ parts as $ part ) {
467
+ if ( isset ( $ registered [ $ part ] ) !== isset ( $ supplied [ $ part ] ) ) {
468
+ $ valid = false ;
469
+ break ;
470
+ }
471
+
472
+ if ( ! isset ( $ registered [ $ part ] ) ) {
473
+ continue ;
474
+ }
475
+
476
+ if ( $ registered [ $ part ] !== $ supplied [ $ part ] ) {
477
+ $ valid = false ;
478
+ break ;
479
+ }
480
+ }
481
+
482
+ /**
483
+ * Filter whether a callback is counted as valid.
484
+ *
485
+ * By default, the URLs must match scheme, host, port, user, pass, and
486
+ * path. Query and fragment segments are allowed to be different.
487
+ *
488
+ * To change this behaviour, filter this value. Note that consumers must
489
+ * have a callback registered, even if you relax this restruction. It is
490
+ * highly recommended not to change this behaviour, as clients will
491
+ * expect the same behaviour across all WP sites.
492
+ *
493
+ * @param boolean $valid True if the callback URL is valid, false otherwise.
494
+ * @param string $url Supplied callback URL.
495
+ * @param WP_Post $consumer Consumer post; stored callback saved as `consumer` meta value.
496
+ */
497
+ return apply_filters ( 'rest_oauth.check_callback ' , $ valid , $ url , $ consumer );
498
+ }
499
+
407
500
/**
408
501
* Authorize a request token
409
502
*
0 commit comments