diff --git a/projects/plugins/jetpack/changelog/dsgcom-534-validate-site-verification-codes b/projects/plugins/jetpack/changelog/dsgcom-534-validate-site-verification-codes
new file mode 100644
index 000000000000..4d82de2c40a9
--- /dev/null
+++ b/projects/plugins/jetpack/changelog/dsgcom-534-validate-site-verification-codes
@@ -0,0 +1,4 @@
+Significance: patch
+Type: bugfix
+
+Site Verification: Reject invalid verification codes instead of reporting a successful save.
diff --git a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php
index 1b1b96fa48d4..1f61f86facee 100644
--- a/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php
+++ b/projects/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php
@@ -1235,6 +1235,21 @@ function ( &$value ) {
case 'verification_services_codes':
$verification_codes = jetpack_verification_validate( $value );
+ foreach ( $value as $service_key => $raw_code ) {
+ if ( '' === $raw_code || null === $raw_code ) {
+ continue;
+ }
+
+ $validated_code = $verification_codes[ $service_key ] ?? '';
+ if ( '' === $validated_code ) {
+ return new WP_Error(
+ 'invalid_input',
+ __( 'Invalid site verification code. Enter only the content value from the meta tag.', 'jetpack' ),
+ 400
+ );
+ }
+ }
+
if ( update_option( 'verification_services_codes', $verification_codes ) ) {
$updated[ $key ] = $verification_codes;
}
diff --git a/projects/plugins/jetpack/modules/verification-tools/verification-tools-utils.php b/projects/plugins/jetpack/modules/verification-tools/verification-tools-utils.php
index 1f916e96a55e..268bcafe284e 100644
--- a/projects/plugins/jetpack/modules/verification-tools/verification-tools-utils.php
+++ b/projects/plugins/jetpack/modules/verification-tools/verification-tools-utils.php
@@ -13,17 +13,25 @@
* @param array $verification_services_codes - array of verification codes.
*/
function jetpack_verification_validate( $verification_services_codes ) {
+ $code_pattern = '/^[a-z0-9_-]+$/i';
+
foreach ( $verification_services_codes as $key => $code ) {
+ $code = is_scalar( $code ) ? (string) $code : '';
+
// Parse html meta tag if it does not look like a valid code.
- if ( ! preg_match( '/^[a-z0-9_-]+$/i', $code ) ) {
+ if ( ! preg_match( $code_pattern, $code ) ) {
$code = jetpack_verification_get_code( $code );
}
- $code = esc_attr( trim( $code ) );
+ $code = esc_attr( trim( (string) $code ) );
// limit length to 100 chars.
$code = substr( $code, 0, 100 );
+ if ( '' !== $code && ! preg_match( $code_pattern, $code ) ) {
+ $code = '';
+ }
+
/**
* Fire after each Verification code was validated.
*
diff --git a/projects/plugins/jetpack/tests/php/json-api/WPCOM_JSON_API_Site_Settings_V1_4_Endpoint_Test.php b/projects/plugins/jetpack/tests/php/json-api/WPCOM_JSON_API_Site_Settings_V1_4_Endpoint_Test.php
index 1e67cc412998..84bd995bb700 100644
--- a/projects/plugins/jetpack/tests/php/json-api/WPCOM_JSON_API_Site_Settings_V1_4_Endpoint_Test.php
+++ b/projects/plugins/jetpack/tests/php/json-api/WPCOM_JSON_API_Site_Settings_V1_4_Endpoint_Test.php
@@ -181,6 +181,44 @@ public function test_post_settings_sets_key_values( $setting_name, $setting_valu
$this->assertSame( $expected_value, $updated[ $setting_name ] );
}
+ /**
+ * Invalid site verification codes return an actionable client error and are not saved.
+ */
+ public function test_post_rejects_invalid_site_verification_code() {
+ $setting = wp_json_encode(
+ array( 'verification_services_codes' => array( 'bing' => 'not.a.valid.token' ) ),
+ JSON_UNESCAPED_SLASHES
+ );
+
+ $response = $this->make_post_request( $setting );
+
+ $this->assertWPError( $response );
+ $this->assertSame( 'invalid_input', $response->get_error_code() );
+ $this->assertSame( 400, $response->get_error_data() );
+ $this->assertFalse( get_option( 'verification_services_codes' ) );
+ }
+
+ /**
+ * A valid site verification meta tag is reduced to its content value and saved.
+ */
+ public function test_post_saves_site_verification_code_from_meta_tag() {
+ $setting = wp_json_encode(
+ array(
+ 'verification_services_codes' => array(
+ 'bing' => '',
+ ),
+ ),
+ JSON_UNESCAPED_SLASHES
+ );
+
+ $response = $this->make_post_request( $setting );
+
+ $this->assertSame(
+ array( 'bing' => '12C1203B5086AECE94EB3A3D9830B2E' ),
+ $response['updated']['verification_services_codes']
+ );
+ }
+
/**
* The free tier description is capped to 500 characters to match the
* paid-tier description field.
diff --git a/projects/plugins/jetpack/tests/php/modules/verification-tools/Jetpack_Verification_Tools_Utils_Test.php b/projects/plugins/jetpack/tests/php/modules/verification-tools/Jetpack_Verification_Tools_Utils_Test.php
index 0327e019dc08..7dd41ea8e533 100644
--- a/projects/plugins/jetpack/tests/php/modules/verification-tools/Jetpack_Verification_Tools_Utils_Test.php
+++ b/projects/plugins/jetpack/tests/php/modules/verification-tools/Jetpack_Verification_Tools_Utils_Test.php
@@ -1,6 +1,7 @@
assertSame(
+ array( $service => $code ),
+ jetpack_verification_validate( array( $service => $code ) )
+ );
+ }
+
+ /**
+ * Verification codes with invalid characters are rejected for every service.
+ *
+ * @dataProvider invalid_verification_code_provider
+ *
+ * @param string $service Verification service key.
+ * @param string $code Verification code.
+ */
+ #[DataProvider( 'invalid_verification_code_provider' )]
+ public function test_invalid_code_is_rejected_for_every_service( $service, $code ) {
+ $this->assertSame(
+ array( $service => '' ),
+ jetpack_verification_validate( array( $service => $code ) )
+ );
+ }
+
+ /**
+ * Provide valid verification codes.
+ *
+ * @return array Test cases.
+ */
+ public static function valid_verification_code_provider() {
+ return array(
+ 'google' => array( 'google', 'verification_Code-123' ),
+ 'bing' => array( 'bing', 'verification_Code-123' ),
+ 'pinterest' => array( 'pinterest', 'verification_Code-123' ),
+ 'yandex' => array( 'yandex', 'verification_Code-123' ),
+ 'facebook' => array( 'facebook', 'verification_Code-123' ),
+ );
+ }
+
+ /**
+ * Provide invalid verification codes.
+ *
+ * @return array Test cases.
+ */
+ public static function invalid_verification_code_provider() {
+ return array(
+ 'google' => array( 'google', 'invalid.code' ),
+ 'bing' => array( 'bing', 'invalid.code' ),
+ 'pinterest' => array( 'pinterest', 'invalid.code' ),
+ 'yandex' => array( 'yandex', 'invalid.code' ),
+ 'facebook' => array( 'facebook', 'invalid.code' ),
+ );
+ }
}