improve request validation and added blacklist check

This commit is contained in:
Matthias Pfefferle 2020-02-21 11:11:03 +01:00
parent 0d48496768
commit 385aac3568
3 changed files with 88 additions and 90 deletions

View file

@ -66,6 +66,11 @@ function init() {
require_once \dirname( __FILE__ ) . '/includes/class-health-check.php';
\Activitypub\Health_Check::init();
require_once \dirname( __FILE__ ) . '/includes/rest/class-server.php';
\add_filter( 'wp_rest_server_class', function() {
return '\Activitypub\Rest\Server';
} );
}
add_action( 'plugins_loaded', '\Activitypub\init' );

View file

@ -14,7 +14,7 @@ class Inbox {
*/
public static function init() {
\add_action( 'rest_api_init', array( '\Activitypub\Rest\Inbox', 'register_routes' ) );
//\add_filter( 'rest_pre_serve_request', array( '\Activitypub\Rest\Inbox', 'serve_request' ), 11, 4 );
\add_filter( 'rest_pre_serve_request', array( '\Activitypub\Rest\Inbox', 'serve_request' ), 11, 4 );
\add_action( 'activitypub_inbox_follow', array( '\Activitypub\Rest\Inbox', 'handle_follow' ), 10, 2 );
\add_action( 'activitypub_inbox_unfollow', array( '\Activitypub\Rest\Inbox', 'handle_unfollow' ), 10, 2 );
//\add_action( 'activitypub_inbox_like', array( '\Activitypub\Rest\Inbox', 'handle_reaction' ), 10, 2 );
@ -36,7 +36,7 @@ class Inbox {
);
\register_rest_route(
'activitypub/1.0', '/users/(?P<id>\d+)/inbox', array(
'activitypub/1.0', '/users/(?P<user_id>\d+)/inbox', array(
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array( '\Activitypub\Rest\Inbox', 'user_inbox' ),
@ -61,10 +61,6 @@ class Inbox {
return $served;
}
if ( 'POST' !== $request->get_method() ) {
return $served;
}
$signature = $request->get_header( 'signature' );
if ( ! $signature ) {
@ -73,6 +69,7 @@ class Inbox {
$headers = $request->get_headers();
// verify signature
//\Activitypub\Signature::verify_signature( $headers, $key );
return $served;
@ -86,21 +83,13 @@ class Inbox {
* @return WP_REST_Response
*/
public static function user_inbox( $request ) {
$author_id = $request->get_param( 'id' );
$user_id = $request->get_param( 'user_id' );
$data = \json_decode( $request->get_body(), true );
$data = $request->get_params();
$type = $request->get_param( 'type' );
if ( ! \is_array( $data ) || ! \array_key_exists( 'type', $data ) ) {
return new \WP_Error( 'rest_invalid_data', \__( 'Invalid payload', 'activitypub' ), array( 'status' => 422 ) );
}
$type = 'create';
if ( ! empty( $data['type'] ) ) {
$type = \strtolower( $data['type'] );
}
\do_action( 'activitypub_inbox', $data, $author_id, $type );
\do_action( "activitypub_inbox_{$type}", $data, $author_id );
\do_action( 'activitypub_inbox', $data, $user_id, $type );
\do_action( "activitypub_inbox_{$type}", $data, $user_id );
return new \WP_REST_Response( array(), 202 );
}
@ -113,61 +102,7 @@ class Inbox {
* @return WP_Error not yet implemented
*/
public static function shared_inbox( $request ) {
$data = \json_decode( $request->get_body(), true );
if ( empty( $data['to'] ) ) {
return new \WP_Error( 'rest_invalid_data', \__( 'No receiving actor set', 'activitypub' ), array( 'status' => 422 ) );
}
if ( \filter_var( $data['to'], \FILTER_VALIDATE_URL ) ) {
$author_id = \Activitypub\url_to_authorid( $data['to'] );
if ( ! $author_id ) {
return new \WP_Error( 'rest_invalid_data', \__( 'No matching user', 'activitypub' ), array( 'status' => 422 ) );
}
} else {
// get the identifier at the left of the '@'
$parts = \explode( '@', $data['to'] );
if ( 3 === \count( $parts ) ) {
$username = $parts[1];
$host = $parts[2];
} elseif ( 2 === \count( $parts ) ) {
$username = $parts[0];
$host = $parts[1];
}
if ( ! $username || ! $host ) {
return new \WP_Error( 'rest_invalid_data', \__( 'Invalid actor identifier', 'activitypub' ), array( 'status' => 422 ) );
}
// check domain
if ( ! \wp_parse_url( \home_url(), \PHP_URL_HOST ) !== $host ) {
return new \WP_Error( 'rest_invalid_data', \__( 'Invalid host', 'activitypub' ), array( 'status' => 422 ) );
}
$author = \get_user_by( 'login', $username );
if ( ! $author ) {
return new \WP_Error( 'rest_invalid_data', \__( 'No matching user', 'activitypub' ), array( 'status' => 422 ) );
}
$author_id = $author->ID;
}
if ( ! \is_array( $data ) || ! \array_key_exists( 'type', $data ) ) {
return new \WP_Error( 'rest_invalid_data', \__( 'Invalid payload', 'activitypub' ), array( 'status' => 422 ) );
}
$type = 'create';
if ( ! empty( $data['type'] ) ) {
$type = \strtolower( $data['type'] );
}
\do_action( 'activitypub_inbox', $data, $author_id, $type );
\do_action( "activitypub_inbox_{$type}", $data, $author_id );
return new \WP_REST_Response( array(), 202 );
}
/**
@ -182,11 +117,54 @@ class Inbox {
'type' => 'integer',
);
$params['id'] = array(
$params['user_id'] = array(
'required' => true,
'type' => 'integer',
);
$params['id'] = array(
'required' => true,
'type' => 'string',
'validate_callback' => function( $param, $request, $key ) {
if ( ! is_string( $param ) ) {
$param = $param['id'];
}
return ! \Activitypub\is_blacklisted( $param );
},
'sanitize_callback' => 'esc_url_raw',
);
$params['actor'] = array(
'required' => true,
'type' => array( 'object', 'string' ),
'validate_callback' => function( $param, $request, $key ) {
if ( ! is_string( $param ) ) {
$param = $param['id'];
}
return ! \Activitypub\is_blacklisted( $param );
},
'sanitize_callback' => function( $param, $request, $key ) {
if ( ! is_string( $param ) ) {
$param = $param['id'];
}
return \esc_url_raw( $param );
},
);
$params['type'] = array(
'required' => true,
'type' => 'enum',
'enum' => array( 'Create' ),
'sanitize_callback' => function( $param, $request, $key ) {
return \strtolower( $param );
},
);
$params['object'] = array(
'required' => true,
'type' => 'object',
);
return $params;
}
@ -197,10 +175,6 @@ class Inbox {
* @param int $user_id The id of the local blog-user
*/
public static function handle_follow( $object, $user_id ) {
if ( ! \array_key_exists( 'actor', $object ) ) {
return new \WP_Error( 'activitypub_no_actor', __( 'No "Actor" found', 'activitypub' ) );
}
// save follower
\Activitypub\Peer\Followers::add_follower( $object['actor'], $user_id );
@ -226,10 +200,6 @@ class Inbox {
* @param int $user_id The id of the local blog-user
*/
public static function handle_unfollow( $object, $user_id ) {
if ( ! \array_key_exists( 'actor', $object ) ) {
return new \WP_Error( 'activitypub_no_actor', \__( 'No "Actor" found', 'activitypub' ) );
}
\Activitypub\Peer\Followers::remove_follower( $object['actor'], $user_id );
}
@ -240,10 +210,6 @@ class Inbox {
* @param int $user_id The id of the local blog-user
*/
public static function handle_reaction( $object, $user_id ) {
if ( ! \array_key_exists( 'actor', $object ) ) {
return new \WP_Error( 'activitypub_no_actor', \__( 'No "Actor" found', 'activitypub' ) );
}
$meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] );
$commentdata = array(
@ -277,10 +243,6 @@ class Inbox {
* @param int $user_id The id of the local blog-user
*/
public static function handle_create( $object, $user_id ) {
if ( ! \array_key_exists( 'actor', $object ) ) {
return new \WP_Error( 'activitypub_no_actor', __( 'No "Actor" found', 'activitypub' ) );
}
$meta = \Activitypub\get_remote_metadata_by_actor( $object['actor'] );
$commentdata = array(

View file

@ -0,0 +1,31 @@
<?php
namespace Activitypub\Rest;
/**
* Custom (hopefully temporary) ActivityPub Rest Server
*
* @author Matthias Pfefferle
*/
class Server extends \WP_REST_Server {
/**
* Overwrite dispatch function to quick fix missing subtype featur
*
* @see https://core.trac.wordpress.org/ticket/49404
*
* @param WP_REST_Request $request Request to attempt dispatching.
* @return WP_REST_Response Response returned by the callback.
*/
public function dispatch( $request ) {
$content_type = $request->get_content_type();
// check for content-sub-types like 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
if ( preg_match( '/application\/([a-zA-Z+_-]+\+)json/', $content_type['value'] ) ) {
$request->set_header( 'Content-Type', 'application/json' );
}
// make request filterable
$request = apply_filters( 'activitypub_pre_dispatch_request', $request );
return parent::dispatch( $request );
}
}