Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Frame_Nonce_Preview
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 6
462
0.00% covered (danger)
0.00%
0 / 1
 get_instance
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
42
 is_frame_nonce_valid
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 maybe_display_post
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 set_post_to_publish
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 handle_autosave_nonce_validation
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2/**
3 * Allows viewing posts on the frontend when the user is not logged in.
4 *
5 * @package automattic/jetpack
6 */
7
8// phpcs:disable WordPress.Security.NonceVerification.Recommended -- This is _implementing_ cross-site nonce handling, no need for WordPress's nonces.
9
10/**
11 * Allows viewing posts on the frontend when the user is not logged in.
12 */
13class Jetpack_Frame_Nonce_Preview {
14    /**
15     * Static instance.
16     *
17     * @todo This should be private.
18     * @var self
19     */
20    public static $instance = null;
21
22    /**
23     * Returns the single instance of the Jetpack_Frame_Nonce_Preview object
24     *
25     * @since 4.3.0
26     *
27     * @return Jetpack_Frame_Nonce_Preview
28     **/
29    public static function get_instance() {
30        if ( null === self::$instance ) {
31            self::$instance = new Jetpack_Frame_Nonce_Preview();
32        }
33
34        return self::$instance;
35    }
36
37    /**
38     * Constructor.
39     *
40     * @todo This should be private.
41     */
42    public function __construct() {
43        if ( isset( $_GET['frame-nonce'] ) && ! is_admin() ) {
44            add_filter( 'pre_get_posts', array( $this, 'maybe_display_post' ) );
45        }
46
47        // autosave previews are validated differently.
48        if ( isset( $_GET['frame-nonce'] ) && isset( $_GET['preview_id'] ) && isset( $_GET['preview_nonce'] ) ) {
49            remove_action( 'init', '_show_post_preview' );
50            add_action( 'init', array( $this, 'handle_autosave_nonce_validation' ) );
51        }
52    }
53
54    /**
55     * Verify that frame nonce exists, and if so, validate the nonce by calling WP.com.
56     *
57     * @since 4.3.0
58     *
59     * @return bool
60     */
61    public function is_frame_nonce_valid() {
62        if ( empty( $_GET['frame-nonce'] ) ) {
63            return false;
64        }
65
66        $xml = new Jetpack_IXR_Client();
67        $xml->query( 'jetpack.verifyFrameNonce', sanitize_key( $_GET['frame-nonce'] ) );
68
69        if ( $xml->isError() ) {
70            return false;
71        }
72
73        return (bool) $xml->getResponse();
74    }
75
76    /**
77     * Conditionally add a hook on posts_results if this is the main query, a preview, and singular.
78     *
79     * @since 4.3.0
80     *
81     * @param WP_Query $query Query.
82     * @return WP_Query
83     */
84    public function maybe_display_post( $query ) {
85        if (
86            $query->is_main_query() &&
87            $query->is_preview() &&
88            $query->is_singular()
89        ) {
90            add_filter( 'posts_results', array( $this, 'set_post_to_publish' ), 10, 2 );
91        }
92
93        return $query;
94    }
95
96    /**
97     * Conditionally set the first post to 'publish' if the frame nonce is valid and there is a post.
98     *
99     * @since 4.3.0
100     *
101     * @param array $posts Posts.
102     * @return array
103     */
104    public function set_post_to_publish( $posts ) {
105        remove_filter( 'posts_results', array( $this, 'set_post_to_publish' ), 10 );
106
107        if ( empty( $posts ) || is_user_logged_in() || ! $this->is_frame_nonce_valid() ) {
108            return $posts;
109        }
110
111        $posts[0]->post_status = 'publish';
112
113        // Disable comments and pings for this post.
114        add_filter( 'comments_open', '__return_false' );
115        add_filter( 'pings_open', '__return_false' );
116
117        return $posts;
118    }
119
120    /**
121     * Handle validation for autosave preview request
122     *
123     * @since 4.7.0
124     */
125    public function handle_autosave_nonce_validation() {
126        if ( ! $this->is_frame_nonce_valid() ) {
127            wp_die( esc_html__( 'Sorry, you are not allowed to preview drafts.', 'jetpack' ) );
128        }
129        add_filter( 'the_preview', '_set_preview' );
130    }
131}
132
133Jetpack_Frame_Nonce_Preview::get_instance();