Skip to content

Security fixes, API compliance, and dependency check - #21

Open
k2sobot wants to merge 5 commits into
PaystackOSS:mainfrom
k2sobot:main
Open

Security fixes, API compliance, and dependency check#21
k2sobot wants to merge 5 commits into
PaystackOSS:mainfrom
k2sobot:main

Conversation

@k2sobot

@k2sobot k2sobot commented Aug 12, 2026

Copy link
Copy Markdown

Summary

This PR addresses critical security vulnerabilities, bug fixes, UI improvements, and adds proper dependency checking to prevent fatal errors when Gravity Forms is not installed.


🔴 Critical Security Fix

Webhook Signature Validation (Timing Attack)

Severity: High

The webhook signature validation was vulnerable to timing attacks using direct string comparison.

// Before (vulnerable)
return $_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] == hash_hmac('sha512', $input, $this->secret_key);

// After (timing-safe)
$signature = isset($_SERVER['HTTP_X_PAYSTACK_SIGNATURE']) ? $_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] : '';
if (empty($signature)) {
    return false;
}
$expected = hash_hmac('sha512', $input, $this->secret_key);
return hash_equals($expected, $signature);

🐛 Bug Fixes

1. Undefined Variable in callback()

The $action array was used conditionally but not initialized, causing PHP warnings:

// Added at start of callback():
$action = array();

2. Input Sanitization

All webhook input data is now properly sanitized:

  • entry_id: Uses absint()
  • reference: Uses sanitize_text_field()

🎨 UI Improvement

Webhook Instructions Inline

The webhook instructions popup (ThickBox) wasn't working properly. Now displays inline:

Before: Click "View Instructions" → popup (often blocked or failed to load)
After: Instructions visible directly on the settings page

// Removed: tb_show() popup JavaScript
// Added: Inline styled instructions with proper security
<p><code style="display: inline-block; padding: 8px 12px; background: #f0f0f1; border-radius: 4px;">
    <?php echo esc_url($this->get_webhook_url(...)); ?>
</code></p>

📋 API Compliance

Billing Intervals

All Paystack-supported intervals are now available:

Interval Description
hourly Every hour
daily Every day
weekly Every week
monthly Every month
quarterly Every 3 months
biannually Every 6 months
annually Every year

Boolean Type Fix

// Before (incorrect - sends string)
$send_invoices = (int) rgar($feed['meta'], 'sendInvoices') == 1 ? 'true' : 'false';

// After (correct - sends boolean)
$send_invoices = (bool) rgar($feed['meta'], 'sendInvoices');

🛡️ Dependency Check

Plugin now fails gracefully when Gravity Forms is not installed:

  1. Admin Notice - Shows error on plugins page/dashboard
  2. Safety Checks - Class files return early if GF not loaded
  3. Helper Function - Returns false instead of fatal error
function gf_paystack() {
    if (!class_exists('GFPaystack')) {
        return false;
    }
    return GFPaystack::get_instance();
}

📝 WordPress Coding Standards

  • All arrays use array() syntax (not [])
  • Proper file docblocks added
  • Consistent indentation
  • Added rel="noopener noreferrer" to external links
  • Escaped output with esc_url()

Files Changed

File Changes
paystack.php Dependency check, admin notice, minimum version
class-gf-paystack.php Safety check, $action init, input sanitization, billing intervals, inline instructions
class-gf-paystack-api.php hash_equals() validation, docblocks
.gitignore Added to block macOS system files

Testing

  • Plugin activates without Gravity Forms (no fatal error)
  • Admin notice displays correctly
  • Webhook validation uses timing-safe comparison
  • All billing intervals available per Paystack API spec
  • Webhook instructions display inline (no popup)
  • External links have proper security attributes

Critical fixes:
- Use hash_equals() for webhook signature validation (prevents timing attacks)
- Initialize $action as array() in callback() to prevent undefined variable errors
- Sanitize entry_id from webhook metadata using absint()
- Sanitize reference strings using sanitize_text_field()

API compliance fixes:
- Remove unsupported billing intervals (hourly, biannually)
- Add quarterly billing interval per Paystack API spec
- Change send_invoices from string to boolean type

WordPress coding standards:
- Use array() syntax instead of [] (WordPress coding standards)
- Add proper docblocks with @throws annotations
- Add GF_PAYSTACK_MIN_GF_VERSION constant for minimum version requirement
- Add admin_notice() to show error if Gravity Forms is not installed
- Add safety check in class-gf-paystack.php to return early if GF not loaded
- Update gf_paystack() helper to return false if class doesn't exist
- Add proper file docblocks to all PHP files (WordPress coding standards)
- Only show admin notice on plugins page or dashboard

Prevents fatal errors when plugin is activated without Gravity Forms.
Paystack supports all these intervals:
- hourly
- daily
- weekly
- monthly
- quarterly
- biannually (every 6 months)
- annually

Previous commit incorrectly removed hourly and biannually.
@k2sobot

k2sobot commented Aug 12, 2026

Copy link
Copy Markdown
Author

Correction: Billing Intervals

I made an error in my original assessment. After reviewing the Paystack API documentation more carefully, Paystack does support all these billing intervals:

  • hourly
  • daily
  • weekly
  • monthly
  • quarterly
  • biannually (every 6 months)
  • annually

I've corrected the code to include all supported intervals. The hourly and biannually options have been restored.

Updated in commit: 3a4bf16


What's Fixed (Still Applies)

  • ✅ Webhook signature validation using hash_equals()
  • ✅ Initialize `` array in callback()
  • ✅ Input sanitization (absint(), sanitize_text_field())
  • ✅ Dependency check for Gravity Forms
  • ✅ Admin notice when GF not installed
  • ✅ WordPress coding standards (array() syntax)

- Remove ThickBox popup (tb_show) that wasn't working
- Display instructions directly inline on settings page
- Add proper styling for webhook URL display
- Add rel='noopener noreferrer' for security
- Escape webhook URL output with esc_url()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant