PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-connection / src / class-server-sandbox.php

class-server-sandbox.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at jetpack_vendor/automattic/jetpack-connection/src/class-server-sandbox.php

244 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The Server_Sandbox class.
4 *
5 * This feature is only useful for Automattic developers.
6 * It configures Jetpack to talk to staging/sandbox servers
7 * on WordPress.com instead of production servers.
8 *
9 * @package automattic/jetpack-sandbox
10 */
11
12 namespace Automattic\Jetpack\Connection;
13
14 use Automattic\Jetpack\Constants;
15
16 /**
17 * The Server_Sandbox class.
18 */
19 class Server_Sandbox {
20
21 /**
22 * Sets up the action hooks for the server sandbox.
23 */
24 public function init() {
25 if ( did_action( 'jetpack_server_sandbox_init' ) ) {
26 return;
27 }
28
29 add_action( 'requests-requests.before_request', array( $this, 'server_sandbox' ), 10, 4 );
30 add_action( 'admin_bar_menu', array( $this, 'admin_bar_add_sandbox_item' ), 999 );
31
32 /**
33 * Fires when the server sandbox is initialized. This action is used to ensure that
34 * the server sandbox action hooks are set up only once.
35 *
36 * @since 1.30.7
37 */
38 do_action( 'jetpack_server_sandbox_init' );
39 }
40
41 /**
42 * Returns the new url and host values.
43 *
44 * @param string $sandbox Sandbox domain.
45 * @param string $url URL of request about to be made.
46 * @param array $headers Headers of request about to be made.
47 * @param string $data The body of request about to be made.
48 * @param string $method The method of request about to be made.
49 *
50 * @return array [ 'url' => new URL, 'host' => new Host, 'new_signature => New signature if url was changed ]
51 */
52 public function server_sandbox_request_parameters( $sandbox, $url, $headers, $data = null, $method = 'GET' ) {
53 $host = '';
54 $new_signature = '';
55
56 if ( ! is_string( $sandbox ) || ! is_string( $url ) ) {
57 return array(
58 'url' => $url,
59 'host' => $host,
60 'new_signature' => $new_signature,
61 );
62 }
63
64 $url_host = wp_parse_url( $url, PHP_URL_HOST );
65
66 switch ( $url_host ) {
67 case 'public-api.wordpress.com':
68 case 'jetpack.wordpress.com':
69 case 'jetpack.com':
70 case 'dashboard.wordpress.com':
71 $host = $headers['Host'] ?? $url_host;
72 $original_url = $url;
73 $url = preg_replace(
74 '@^(https?://)' . preg_quote( $url_host, '@' ) . '(?=[/?#].*|$)@',
75 '${1}' . $sandbox,
76 $url,
77 1
78 );
79
80 /**
81 * Whether to add the X Debug query parameter to the request made to the Sandbox
82 *
83 * @since 1.36.0
84 *
85 * @param bool $add_parameter Whether to add the parameter to the request or not. Default is to false.
86 * @param string $url The URL of the request being made.
87 * @param string $host The host of the request being made.
88 */
89 if ( apply_filters( 'jetpack_sandbox_add_profile_parameter', false, $url, $host ) ) {
90 $url = add_query_arg( 'XDEBUG_PROFILE', 1, $url );
91
92 // URL has been modified since the signature was created. We'll need a new one.
93 $original_url = add_query_arg( 'XDEBUG_PROFILE', 1, $original_url );
94 $new_signature = $this->get_new_signature( $original_url, $headers, $data, $method );
95
96 }
97 }
98
99 return compact( 'url', 'host', 'new_signature' );
100 }
101
102 /**
103 * Gets a new signature for the request
104 *
105 * @param string $url The new URL to be signed.
106 * @param array $headers The headers of the request about to be made.
107 * @param string $data The body of request about to be made.
108 * @param string $method The method of the request about to be made.
109 * @return string|null
110 */
111 private function get_new_signature( $url, $headers, $data, $method ) {
112
113 if ( ! empty( $headers['Authorization'] ) ) {
114 $a_headers = $this->extract_authorization_headers( $headers );
115 if ( ! empty( $a_headers ) ) {
116 $token_details = explode( ':', $a_headers['token'] );
117
118 if ( count( $token_details ) === 3 ) {
119 $user_id = $token_details[2];
120 $token = ( new Tokens() )->get_access_token( $user_id );
121 $time_diff = (int) \Jetpack_Options::get_option( 'time_diff' );
122 $jetpack_signature = new \Jetpack_Signature( $token->secret, $time_diff );
123
124 $signature = $jetpack_signature->sign_request(
125 $a_headers['token'],
126 $a_headers['timestamp'],
127 $a_headers['nonce'],
128 $a_headers['body-hash'],
129 $method,
130 $url,
131 $data,
132 false
133 );
134
135 if ( $signature && ! is_wp_error( $signature ) ) {
136 return $signature;
137 } elseif ( is_wp_error( $signature ) ) {
138 $this->log_new_signature_error( $signature->get_error_message() );
139 }
140 } else {
141 $this->log_new_signature_error( 'Malformed token on Authorization Header' );
142 }
143 } else {
144 $this->log_new_signature_error( 'Error extracting Authorization Header' );
145 }
146 } else {
147 $this->log_new_signature_error( 'Empty Authorization Header' );
148 }
149 }
150
151 /**
152 * Logs error if the attempt to create a new signature fails
153 *
154 * @param string $message The error message.
155 * @return void
156 */
157 private function log_new_signature_error( $message ) {
158 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
159 error_log( sprintf( "SANDBOXING: Error re-signing the request. '%s'", $message ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
160 }
161 }
162
163 /**
164 * Extract the values in the Authorization header into an array
165 *
166 * @param array $headers The headers of the request about to be made.
167 * @return array|null
168 */
169 public function extract_authorization_headers( $headers ) {
170 if ( ! empty( $headers['Authorization'] ) && is_string( $headers['Authorization'] ) ) {
171 $header = str_replace( 'X_JETPACK ', '', $headers['Authorization'] );
172 $vars = explode( ' ', $header );
173 $result = array();
174 foreach ( $vars as $var ) {
175 $elements = explode( '"', $var );
176 if ( count( $elements ) === 3 ) {
177 $result[ substr( $elements[0], 0, -1 ) ] = $elements[1];
178 }
179 }
180 return $result;
181 }
182 }
183
184 /**
185 * Modifies parameters of request in order to send the request to the
186 * server specified by `JETPACK__SANDBOX_DOMAIN`.
187 *
188 * Attached to the `requests-requests.before_request` filter.
189 *
190 * @param string $url URL of request about to be made.
191 * @param array $headers Headers of request about to be made.
192 * @param array|string $data Data of request about to be made.
193 * @param string $type Type of request about to be made.
194 * @return void
195 */
196 public function server_sandbox( &$url, &$headers, &$data = null, &$type = null ) {
197 if ( ! Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ) ) {
198 return;
199 }
200
201 $original_url = $url;
202
203 $request_parameters = $this->server_sandbox_request_parameters( Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ), $url, $headers, $data, $type );
204
205 $url = $request_parameters['url'];
206
207 if ( $request_parameters['host'] ) {
208 $headers['Host'] = $request_parameters['host'];
209
210 if ( $request_parameters['new_signature'] ) {
211 $headers['Authorization'] = preg_replace( '/signature=\"[^\"]+\"/', 'signature="' . $request_parameters['new_signature'] . '"', $headers['Authorization'] );
212 }
213
214 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
215 error_log( sprintf( "SANDBOXING via '%s': '%s'", Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ), $original_url ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
216 }
217 }
218 }
219
220 /**
221 * Adds a "Jetpack API Sandboxed" item to the admin bar if the JETPACK__SANDBOX_DOMAIN
222 * constant is set.
223 *
224 * Attached to the `admin_bar_menu` action.
225 *
226 * @param \WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
227 */
228 public function admin_bar_add_sandbox_item( $wp_admin_bar ) {
229 if ( ! Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ) ) {
230 return;
231 }
232
233 $node = array(
234 'id' => 'jetpack-connection-api-sandbox',
235 'title' => 'Jetpack API Sandboxed',
236 'meta' => array(
237 'title' => 'Sandboxing via ' . Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ),
238 ),
239 );
240
241 $wp_admin_bar->add_menu( $node );
242 }
243 }
244