Skip to content

Commit

Permalink
Merge pull request #210 from wp-graphql/fix/wporg-submission-feedback
Browse files Browse the repository at this point in the history
feat: WordPress.org Feedback (2nd pass)
  • Loading branch information
jasonbahl authored Sep 23, 2024
2 parents ab126d3 + 29d7ed1 commit 5819216
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 16 deletions.
11 changes: 11 additions & 0 deletions .changeset/hip-flies-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"wpgraphql-ide": patch
---

### Added

- Introduced `wp_localize_escaped_data()` function for recursively escaping data before localizing it in WordPress. This ensures safe output of strings, URLs, integers, and nested arrays when passing data to JavaScript, using native WordPress functions like `wp_kses_post()` and `esc_url()`.

### Improved

- Enhanced security by ensuring all localized data is properly sanitized before being passed to `wp_localize_script()`, preventing potential XSS vulnerabilities and ensuring safe use of dynamic data in JavaScript.
73 changes: 57 additions & 16 deletions wpgraphql-ide.php
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ function reorder_graphql_submenu_items(): void {
$ordered_submenu[] = $graphql_ide;
}
if ( 'on' === $show_legacy_editor && $graphiql_ide ) {
$graphiql_ide[0] = 'Legacy GraphQL IDE';
$graphiql_ide[0] = esc_html__( 'Legacy GraphQL IDE', 'wpgraphql-ide' );
$ordered_submenu[] = $graphiql_ide;
}
if ( $extensions ) {
Expand Down Expand Up @@ -386,7 +386,7 @@ function enqueue_graphql_ide_menu_icon_css(): void {
}
';

wp_add_inline_style( 'admin-bar', $custom_css );
wp_add_inline_style( 'admin-bar', wp_kses_post( $custom_css ) );
}

/**
Expand Down Expand Up @@ -445,10 +445,12 @@ function enqueue_react_app_with_styles(): void {
'dedicatedIdeBaseUrl' => get_dedicated_ide_base_url(),
];

$escaped_data = wp_localize_escaped_data( $localized_data );

wp_localize_script(
'wpgraphql-ide',
'WPGRAPHQL_IDE_DATA',
$localized_data
$escaped_data
);

// Extensions looking to extend GraphiQL can hook in here,
Expand Down Expand Up @@ -505,6 +507,46 @@ function get_plugin_header( string $key = '' ): ?string {
return is_string( $plugin_header ) ? $plugin_header : null;
}

/**
* Retrieves and sanitizes external fragments.
*
* @return array<string> The sanitized array of external fragments.
*/
function get_external_fragments(): array {
// Retrieve external fragments using the filter.
$external_fragments = apply_filters( 'wpgraphql_ide_external_fragments', [] );

// Loop through each fragment, sanitize, and ensure it's a valid GraphQL fragment.
return array_filter(
array_map( 'sanitize_text_field', $external_fragments ),
static function ( string $fragment ): bool {
// Check if the fragment starts with "fragment" and contains "on" (basic GraphQL fragment validation).
return preg_match( '/^fragment\s+\w+\s+on\s+\w+\s*{/', trim( $fragment ) ) === 1;
}
);
}

/**
* Recursive function to escape an array or value for safe output, specifically for localizing data in WordPress.
*
* @param mixed $data The data to escape.
* @return mixed The escaped data.
*/
function wp_localize_escaped_data( $data ) {
if ( is_array( $data ) ) {
return array_map( __NAMESPACE__ . '\wp_localize_escaped_data', $data );
} elseif ( is_string( $data ) ) {
// Use wp_kses_post to allow basic HTML for content and esc_url for URLs
return filter_var( $data, FILTER_VALIDATE_URL ) ? esc_url( $data ) : wp_kses_post( $data );
} elseif ( is_int( $data ) ) {
return absint( $data );
} elseif ( is_bool( $data ) ) {
return (bool) $data;
}

return $data; // Return original value if it's not a string, int, or bool.
}

/**
* Retrieves app context.
*
Expand All @@ -514,18 +556,17 @@ function get_app_context(): array {
$current_user = wp_get_current_user();

// Get the avatar URL for the current user. Returns an empty string if no user is logged in.
$avatar_url = $current_user->exists() ? get_avatar_url( $current_user->ID ) : '';

return apply_filters(
'wpgraphql_ide_context',
[
'pluginVersion' => get_plugin_header( 'Version' ),
'pluginName' => get_plugin_header( 'Name' ),
'externalFragments' => apply_filters( 'wpgraphql_ide_external_fragments', [] ),
'avatarUrl' => $avatar_url,
'drawerButtonLabel' => __( 'GraphQL IDE', 'wpgraphql-ide' ),
]
);
$avatar_url = $current_user->exists() ? ( get_avatar_url( $current_user->ID ) ?: '' ) : '';

$app_context = [
'pluginVersion' => get_plugin_header( 'Version' ),
'pluginName' => get_plugin_header( 'Name' ),
'externalFragments' => get_external_fragments(),
'avatarUrl' => $avatar_url,
'drawerButtonLabel' => __( 'GraphQL IDE', 'wpgraphql-ide' ),
];

return apply_filters( 'wpgraphql_ide_context', $app_context );
}

/**
Expand Down Expand Up @@ -558,7 +599,7 @@ function graphql_admin_notices_render_notices( array $notices ): void {
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
wp_register_style( 'wpgraphql-ide-admin-notices', false );
wp_enqueue_style( 'wpgraphql-ide-admin-notices' );
wp_add_inline_style( 'wpgraphql-ide-admin-notices', $custom_css );
wp_add_inline_style( 'wpgraphql-ide-admin-notices', wp_kses_post( $custom_css ) );
}

/**
Expand Down

0 comments on commit 5819216

Please sign in to comment.