Skip to content

fix: address security vulnerabilities in webhooks plugin #336

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

josephfusco
Copy link
Member

@josephfusco josephfusco commented Jul 11, 2025

This pull request introduces security enhancements and debugging improvements to the wp-graphql-webhooks plugin. Key changes include improved input/output sanitization, stricter authorization checks, conditional logging for debugging, and adjustments to ensure sensitive information is not exposed in logs.

Copy link

changeset-bot bot commented Jul 11, 2025

🦋 Changeset detected

Latest commit: cff50ab

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@wpengine/wpgraphql-webhooks-wordpress-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

github-actions bot commented Jul 11, 2025

ℹ️ Download the latest wp-graphql-webhooks plugin zip from this PR
(See the 'Artifacts' section at the bottom)

@github-project-automation github-project-automation bot moved this to 🆕 Backlog in Headless OSS Jul 14, 2025
@josephfusco josephfusco marked this pull request as ready for review July 15, 2025 14:06
@josephfusco josephfusco requested a review from a team as a code owner July 15, 2025 14:06
@josephfusco josephfusco requested review from theodesp and Copilot July 15, 2025 14:11
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR strengthens security in the wp-graphql-webhooks plugin by sanitizing inputs/outputs, tightening authorization checks, and ensuring sensitive data isn’t logged by default.

  • Added esc_html and proper sanitization functions to exception messages, nonces, and user inputs
  • Wrapped debug logs in WP_DEBUG checks and removed sensitive URL/headers from default logs
  • Replaced capability checks and nonce verifications for admin actions

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
plugins/wp-graphql-webhooks/src/Services/PluginServiceLocator.php Escape exception messages with esc_html to prevent HTML injection
plugins/wp-graphql-webhooks/src/Rest/WebhookTestEndpoint.php Conditional debug logging, redact sensitive URL/headers, and static triggered_by
plugins/wp-graphql-webhooks/src/Handlers/WebhookHandler.php Wrapped dispatch logs in WP_DEBUG checks, but still logs sensitive URL/headers
plugins/wp-graphql-webhooks/src/Events/SmartCacheWebhookManager.php Added debug log guard and switched from parse_url to wp_parse_url
plugins/wp-graphql-webhooks/src/Admin/WebhooksListTable.php Sanitized orderby/order, escaped nonces, and outputs for bulk actions
plugins/wp-graphql-webhooks/src/Admin/WebhooksAdmin.php Improved nonce unslashing, but replaced capability check with only nonce verification
.changeset/spotty-mice-behave.md Added changelog entries for security hardening

Comment on lines 28 to +29
if (!isset($this->factories[$name])) {
throw new UnexpectedValueException("Service not found: {$name}");
throw new UnexpectedValueException( esc_html( "Service not found: {$name}" ) );
Copy link
Preview

Copilot AI Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using esc_html inside exception logic is meant for HTML output escaping; consider sanitizing the input earlier (e.g., with sanitize_text_field) or omitting HTML-specific escaping here.

Copilot uses AI. Check for mistakes.

@@ -107,7 +108,7 @@ public function test_webhook( WP_REST_Request $request ): WP_REST_Response|WP_Er
],
'test_data' => [
'message' => 'This is a test webhook payload',
'triggered_by' => wp_get_current_user()->user_login,
'triggered_by' => 'admin',
Copy link
Preview

Copilot AI Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoding 'admin' for triggered_by loses the actual user identity; consider using the sanitized current user login or ID to preserve accurate audit data.

Suggested change
'triggered_by' => 'admin',
'triggered_by' => sanitize_text_field( wp_get_current_user()->user_login ?: 'anonymous' ),

Copilot uses AI. Check for mistakes.

error_log( "Timestamp: {$dispatch_timestamp}" );
error_log( "Webhook: {$webhook->name} (ID: {$webhook->id})" );
error_log( "Event: {$webhook->event}" );
error_log( "Target URL: {$webhook->url}" );
Copy link
Preview

Copilot AI Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging the target URL may expose sensitive endpoints; wrap this in the WP_DEBUG check or remove it to avoid leaking URLs in production logs.

Suggested change
error_log( "Target URL: {$webhook->url}" );
// Removed logging of Target URL to prevent exposure of sensitive endpoints.

Copilot uses AI. Check for mistakes.

}

// Log headers
error_log( "Headers: " . wp_json_encode( $args['headers'] ) );
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( "Headers: " . wp_json_encode( $args['headers'] ) );
Copy link
Preview

Copilot AI Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging headers can reveal sensitive authentication tokens; redact or omit header values unless explicitly needed for debugging behind WP_DEBUG.

Suggested change
error_log( "Headers: " . wp_json_encode( $args['headers'] ) );
$redacted_headers = array_map(
function( $value, $key ) {
// Redact sensitive headers
$sensitive_keys = [ 'Authorization', 'Cookie', 'Set-Cookie' ];
return in_array( $key, $sensitive_keys, true ) ? '[REDACTED]' : $value;
},
$args['headers'],
array_keys( $args['headers'] )
);
error_log( "Headers: " . wp_json_encode( $redacted_headers ) );

Copilot uses AI. Check for mistakes.

@@ -375,7 +370,7 @@ public function ajax_test_webhook(): void {
] );
}

if ( ! current_user_can( 'manage_options' ) ) {
if ( ! $this->verify_admin_permission() ) {
Copy link
Preview

Copilot AI Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced current_user_can('manage_options') with only nonce verification, dropping capability checks; restore the user capability check to prevent unauthorized access.

Suggested change
if ( ! $this->verify_admin_permission() ) {
if ( ! current_user_can( 'manage_options' ) ) {

Copilot uses AI. Check for mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 🆕 Backlog
Development

Successfully merging this pull request may close these issues.

2 participants