Compare commits

...

2 commits

Author SHA1 Message Date
Matthias Pfefferle
8f897f5578 Add default query validation rules 2023-08-29 12:50:17 +02:00
Matthias Pfefferle
1397d864bb First draft of an Activity-Sanitizer 2023-08-07 15:47:22 +02:00
11 changed files with 265 additions and 162 deletions

View file

@ -68,7 +68,7 @@ class Activity extends Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | null * | null
*/ */
@ -82,7 +82,7 @@ class Activity extends Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object-term * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object-term
* *
* @var string * @var string
* | Base_Objectr * | Base_Object
* | Link * | Link
* | null * | null
*/ */
@ -97,7 +97,7 @@ class Activity extends Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-actor * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-actor
* *
* @var string * @var string
* | \ActivityPhp\Type\Extended\AbstractActor * | Actor
* | array<Actor> * | array<Actor>
* | array<Link> * | array<Link>
* | Link * | Link
@ -116,8 +116,8 @@ class Activity extends Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-target * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-target
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | array<ObjectType> * | array<Base_Object>
* | Link * | Link
* | array<Link> * | array<Link>
*/ */
@ -132,7 +132,7 @@ class Activity extends Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-result * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-result
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | null * | null
*/ */
@ -149,7 +149,7 @@ class Activity extends Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-origin * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-origin
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | null * | null
*/ */
@ -162,7 +162,7 @@ class Activity extends Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-instrument * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-instrument
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | null * | null
*/ */
@ -176,7 +176,7 @@ class Activity extends Base_Object {
* *
* @see https://www.w3.org/TR/activitypub/#object-without-create * @see https://www.w3.org/TR/activitypub/#object-without-create
* *
* @param string|Base_Objectr|Link|null $object * @param string|Base_Object|Link|null $object
* *
* @return void * @return void
*/ */

View file

@ -49,9 +49,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attachment * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attachment
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -65,9 +65,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attributedto * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attributedto
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -80,9 +80,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audience * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audience
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -115,7 +115,7 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | null * | null
*/ */
@ -210,9 +210,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-inreplyto * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-inreplyto
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -225,9 +225,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-location * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-location
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -239,7 +239,7 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-preview * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-preview
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | null * | null
*/ */
@ -287,7 +287,7 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-summary * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-summary
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | null * | null
*/ */
@ -313,9 +313,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tag * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tag
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -348,9 +348,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-to * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-to
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -363,9 +363,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-bto * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-bto
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -378,9 +378,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-cc * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-cc
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -393,9 +393,9 @@ class Base_Object {
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-bcc * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-bcc
* *
* @var string * @var string
* | ObjectType * | Base_Object
* | Link * | Link
* | array<ObjectType> * | array<Base_Object>
* | array<Link> * | array<Link>
* | null * | null
*/ */
@ -433,7 +433,7 @@ class Base_Object {
* *
* @see https://www.w3.org/TR/activitypub/#source-property * @see https://www.w3.org/TR/activitypub/#source-property
* *
* @var ObjectType * @var Base_Object
*/ */
protected $source; protected $source;

View file

@ -0,0 +1,136 @@
<?php
namespace Activitypub;
class Sanitizer {
/**
* Sanitize a multi-dimensional array
*
* @param array $array The array to sanitize.
*
* @return array The sanitized array.
*/
public static function sanitize_array( $array ) {
$sanitized_array = array();
foreach ( $array as $key => $value ) {
$key = self::sanitize_key( $key );
if (
in_array(
$key,
array(
'summary_map',
'summaryMap',
'content_map',
'contentMap',
),
true
)
) {
$sanitized_array[ $key ] = self::sanitize_map( $value );
} elseif (
in_array(
$key,
array(
'inbox',
'outbox',
'followers',
'following',
),
true
)
) {
if ( is_string( $value ) ) {
$sanitized_array[ $key ] = sanitize_url( $value );
} else {
$sanitized_array[ $key ] = '';
}
} elseif ( in_array( $key, array( 'summary', 'content' ), true ) ) {
$sanitized_array[ $key ] = self::sanitize_html( $value );
} elseif ( is_array( $value ) ) {
$sanitized_array[ $key ] = self::sanitize_array( $value );
} else {
$sanitized_array[ $key ] = self::sanitize_value( $value );
}
}
return $sanitized_array;
}
/**
* Sanitize a value.
*
* @param string $value The value to sanitize.
*
* @return string The sanitized value.
*/
public static function sanitize_value( $value ) {
if ( is_email( $value ) ) {
return sanitize_email( $value );
}
if ( filter_var( $value, FILTER_VALIDATE_URL ) ) {
return sanitize_url( $value );
}
return sanitize_text_field( $value );
}
/**
* Sanitize HTML.
*
* @param string $value The value to sanitize.
*
* @return string The sanitized value.
*/
public static function sanitize_html( $value ) {
if ( is_array( $value ) ) {
return '';
}
global $allowedtags;
$tags = array_merge(
$allowedtags,
array( 'p' => array() )
);
$value = \preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $value );
$value = \strip_shortcodes( $value );
$value = \wptexturize( $value );
$value = \wp_kses( $value, $tags );
return $value;
}
/**
* Sanitize a translation map
*
* @param array $map The map to sanitize.
*
* @return array The sanitized map.
*/
public static function sanitize_map( $map ) {
$sanitized_map = array();
foreach ( $map as $key => $value ) {
$key = self::sanitize_key( $key );
$sanitized_map[ $key ] = self::sanitize_html( $value );
}
return $sanitized_map;
}
/**
* Sanitize an array key
*
* @param string $key The key to sanitize.
*
* @return string The sanitized key.
*/
public static function sanitize_key( $key ) {
return \preg_replace( '/[^a-zA-Z0-9_\-]/', '', $key );
}
}

View file

@ -5,6 +5,7 @@ use WP_Error;
use stdClass; use stdClass;
use WP_REST_Server; use WP_REST_Server;
use WP_REST_Response; use WP_REST_Response;
use Activitypub\Validator\Query;
use Activitypub\Collection\Users as User_Collection; use Activitypub\Collection\Users as User_Collection;
use Activitypub\Collection\Followers as Follower_Collection; use Activitypub\Collection\Followers as Follower_Collection;
@ -36,7 +37,7 @@ class Followers {
array( array(
'methods' => WP_REST_Server::READABLE, 'methods' => WP_REST_Server::READABLE,
'callback' => array( self::class, 'get' ), 'callback' => array( self::class, 'get' ),
'args' => self::request_parameters(), 'args' => Query::get_default_args(),
'permission_callback' => '__return_true', 'permission_callback' => '__return_true',
), ),
) )
@ -108,42 +109,4 @@ class Followers {
return $response; return $response;
} }
/**
* The supported parameters
*
* @return array list of parameters
*/
public static function request_parameters() {
$params = array();
$params['page'] = array(
'type' => 'integer',
'default' => 1,
);
$params['per_page'] = array(
'type' => 'integer',
'default' => 20,
);
$params['order'] = array(
'type' => 'string',
'default' => 'desc',
'enum' => array( 'asc', 'desc' ),
);
$params['user_id'] = array(
'required' => true,
'type' => 'string',
);
$params['context'] = array(
'type' => 'string',
'default' => 'simple',
'enum' => array( 'simple', 'full' ),
);
return $params;
}
} }

View file

@ -1,6 +1,7 @@
<?php <?php
namespace Activitypub\Rest; namespace Activitypub\Rest;
use Activitypub\Validator\Query;
use Activitypub\Collection\Users as User_Collection; use Activitypub\Collection\Users as User_Collection;
use function Activitypub\get_rest_url_by_path; use function Activitypub\get_rest_url_by_path;
@ -31,7 +32,7 @@ class Following {
array( array(
'methods' => \WP_REST_Server::READABLE, 'methods' => \WP_REST_Server::READABLE,
'callback' => array( self::class, 'get' ), 'callback' => array( self::class, 'get' ),
'args' => self::request_parameters(), 'args' => Query::get_default_args(),
'permission_callback' => '__return_true', 'permission_callback' => '__return_true',
), ),
) )
@ -78,24 +79,4 @@ class Following {
return $response; return $response;
} }
/**
* The supported parameters
*
* @return array list of parameters
*/
public static function request_parameters() {
$params = array();
$params['page'] = array(
'type' => 'integer',
);
$params['user_id'] = array(
'required' => true,
'type' => 'string',
);
return $params;
}
} }

View file

@ -4,6 +4,7 @@ namespace Activitypub\Rest;
use WP_Error; use WP_Error;
use WP_REST_Server; use WP_REST_Server;
use WP_REST_Response; use WP_REST_Response;
use Activitypub\Validator\Query;
use Activitypub\Activity\Activity; use Activitypub\Activity\Activity;
use Activitypub\Collection\Users as User_Collection; use Activitypub\Collection\Users as User_Collection;
@ -59,7 +60,7 @@ class Inbox {
array( array(
'methods' => WP_REST_Server::READABLE, 'methods' => WP_REST_Server::READABLE,
'callback' => array( self::class, 'user_inbox_get' ), 'callback' => array( self::class, 'user_inbox_get' ),
'args' => self::user_inbox_get_parameters(), 'args' => Query::get_default_args(),
'permission_callback' => '__return_true', 'permission_callback' => '__return_true',
), ),
) )
@ -180,26 +181,6 @@ class Inbox {
return new WP_REST_Response( array(), 202 ); return new WP_REST_Response( array(), 202 );
} }
/**
* The supported parameters
*
* @return array list of parameters
*/
public static function user_inbox_get_parameters() {
$params = array();
$params['page'] = array(
'type' => 'integer',
);
$params['user_id'] = array(
'required' => true,
'type' => 'string',
);
return $params;
}
/** /**
* The supported parameters * The supported parameters
* *

View file

@ -5,6 +5,7 @@ use stdClass;
use WP_Error; use WP_Error;
use WP_REST_Server; use WP_REST_Server;
use WP_REST_Response; use WP_REST_Response;
use Activitypub\Validator\Query;
use Activitypub\Transformer\Post; use Activitypub\Transformer\Post;
use Activitypub\Activity\Activity; use Activitypub\Activity\Activity;
use Activitypub\Collection\Users as User_Collection; use Activitypub\Collection\Users as User_Collection;
@ -38,7 +39,7 @@ class Outbox {
array( array(
'methods' => WP_REST_Server::READABLE, 'methods' => WP_REST_Server::READABLE,
'callback' => array( self::class, 'user_outbox_get' ), 'callback' => array( self::class, 'user_outbox_get' ),
'args' => self::request_parameters(), 'args' => Query::get_default_args(),
'permission_callback' => '__return_true', 'permission_callback' => '__return_true',
), ),
) )
@ -129,25 +130,4 @@ class Outbox {
return $response; return $response;
} }
/**
* The supported parameters
*
* @return array list of parameters
*/
public static function request_parameters() {
$params = array();
$params['page'] = array(
'type' => 'integer',
'default' => 1,
);
$params['user_id'] = array(
'required' => true,
'type' => 'string',
);
return $params;
}
} }

View file

@ -4,6 +4,7 @@ namespace Activitypub\Rest;
use WP_Error; use WP_Error;
use WP_REST_Server; use WP_REST_Server;
use WP_REST_Response; use WP_REST_Response;
use Activitypub\Validator\Query;
use Activitypub\Activity\Activity; use Activitypub\Activity\Activity;
use Activitypub\Collection\Users as User_Collection; use Activitypub\Collection\Users as User_Collection;
@ -35,7 +36,7 @@ class Users {
array( array(
'methods' => WP_REST_Server::READABLE, 'methods' => WP_REST_Server::READABLE,
'callback' => array( self::class, 'get' ), 'callback' => array( self::class, 'get' ),
'args' => self::request_parameters(), 'args' => Query::get_default_args(),
'permission_callback' => '__return_true', 'permission_callback' => '__return_true',
), ),
) )
@ -79,24 +80,4 @@ class Users {
return $response; return $response;
} }
/**
* The supported parameters
*
* @return array list of parameters
*/
public static function request_parameters() {
$params = array();
$params['page'] = array(
'type' => 'string',
);
$params['user_id'] = array(
'required' => true,
'type' => 'string',
);
return $params;
}
} }

View file

@ -37,7 +37,7 @@ class Webfinger {
array( array(
'methods' => \WP_REST_Server::READABLE, 'methods' => \WP_REST_Server::READABLE,
'callback' => array( self::class, 'webfinger' ), 'callback' => array( self::class, 'webfinger' ),
'args' => self::request_parameters(), 'args' => self::request_args(),
'permission_callback' => '__return_true', 'permission_callback' => '__return_true',
), ),
) )
@ -68,16 +68,16 @@ class Webfinger {
* *
* @return array list of parameters * @return array list of parameters
*/ */
public static function request_parameters() { public static function request_args() {
$params = array(); $args = array();
$params['resource'] = array( $args['resource'] = array(
'required' => true, 'required' => true,
'type' => 'string', 'type' => 'string',
'pattern' => '^acct:(.+)@(.+)$', 'pattern' => '^acct:(.+)@(.+)$',
); );
return $params; return $args;
} }
/** /**

View file

@ -0,0 +1,38 @@
<?php
namespace Activitypub\Validator;
class Query {
/**
* Validate the query parameters.
*
* @return array A list of arguments and how to validate them.
*/
public static function get_default_args() {
$args = array();
$args['page'] = array(
'type' => 'integer',
'default' => 1,
);
$args['per_page'] = array(
'type' => 'integer',
'default' => 20,
);
$args['order'] = array(
'type' => 'string',
'default' => 'desc',
'enum' => array( 'asc', 'desc' ),
);
$args['context'] = array(
'type' => 'string',
'default' => 'simple',
'enum' => array( 'simple', 'full' ),
);
return $args;
}
}

View file

@ -0,0 +1,43 @@
<?php
class Test_Activitypub_Sanitizer extends WP_UnitTestCase {
/**
* @dataProvider the_data_provider
*/
public function test_sanitize_array( $source, $target ) {
$sanitizer = new Activitypub\Sanitizer();
$this->assertEquals( $target, $sanitizer->sanitize_array( $source ) );
}
public function the_data_provider() {
return array(
array(
array(
'type"§$' => '<p>Create</p>',
'content' => '<p>Content</p><script>content</script>',
'contentMap' => array(
'en' => '<p>Content</p><script>content</script>',
),
'nameMap' => array(
'en' => '<div>Content</div><script>content</script>',
),
'inbox' => 'https://example.org/inbox',
'outbox' => 'example.org/outbox',
'name' => 'Gifts\'+OR+1=1--',
),
array(
'type' => 'Create',
'content' => '<p>Content</p>',
'contentMap' => array(
'en' => '<p>Content</p>',
),
'nameMap' => array(
'en' => 'Content',
),
'inbox' => 'https://example.org/inbox',
'outbox' => 'http://example.org/outbox',
'name' => 'Gifts\'+OR+1=1--',
),
),
);
}
}