Changeset 3458129
- Timestamp:
- 02/10/2026 02:47:49 PM (9 days ago)
- Location:
- spamjam/trunk
- Files:
-
- 2 deleted
- 9 edited
-
changelog.txt (modified) (1 diff)
-
composer.lock (modified) (1 diff)
-
includes/admin-tabs.php (modified) (24 diffs)
-
includes/admin.php (modified) (1 diff)
-
includes/migration.php (modified) (4 diffs)
-
includes/settings-schema.php (modified) (1 diff)
-
prepare_release.sh (deleted)
-
readme.txt (modified) (3 diffs)
-
spamjam.code-workspace (deleted)
-
spamjam.php (modified) (6 diffs)
-
vendor/composer/installed.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
spamjam/trunk/changelog.txt
r3428403 r3458129 1 = 1.1.0 - 2025-02-10 = 2 * Security - Fixed SQL injection risk: all database queries now use $wpdb->prepare() with %i/%s placeholders 3 * Security - Fixed email confirmation auth flaw: replaced expiring nonce with persistent hashed token 4 * Security - Fixed premium plan bypass via HTTP Host header spoofing 5 * Security - Sanitized all $_SERVER['REMOTE_ADDR'] usage via centralized get_client_ip() helper 6 * Security - Added wp_unslash() to all raw $_POST accesses and nonce verifications 7 * Security - Escaped all admin notice output with esc_html__() in multisite sync 8 * WP VIP - Replaced file_get_contents(), rename(), unlink() with WP_Filesystem API in migration 9 * WPCS - Replaced all _e() with esc_html_e() in admin templates 10 * WPCS - Escaped $disabled output with esc_attr() in all form fields 11 * WPCS - Replaced date() with gmdate() in advanced reporting 12 * WPCS - Replaced deprecated current_time('timestamp') with time() 13 * Enhancement - Added WooCommerce product_instance_caching compatibility 14 * Enhancement - Added WooCommerce cart_checkout_blocks compatibility 15 1 16 = 1.0.7 - 2025-12-27 = 2 17 * Fixed - Field swap hook changed from pre_comment_on_post to init for better compatibility -
spamjam/trunk/composer.lock
r3427664 r3458129 699 699 "platform": {}, 700 700 "platform-dev": {}, 701 "plugin-api-version": "2. 6.0"701 "plugin-api-version": "2.9.0" 702 702 } -
spamjam/trunk/includes/admin-tabs.php
r3427664 r3458129 99 99 $total = 0; 100 100 101 if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) === $table_name ) { 101 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 102 if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) === $table_name ) { 102 103 // Optimized: Single query instead of 4 separate queries (75% faster). 104 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 103 105 $stats = $wpdb->get_row( 104 "SELECT 105 COUNT(*) as total, 106 SUM(CASE WHEN DATE(created_at) = CURDATE() THEN 1 ELSE 0 END) as today, 107 SUM(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 1 ELSE 0 END) as week, 108 SUM(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as month 109 FROM $table_name" 106 $wpdb->prepare( 107 'SELECT 108 COUNT(*) as total, 109 SUM(CASE WHEN DATE(created_at) = CURDATE() THEN 1 ELSE 0 END) as today, 110 SUM(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 1 ELSE 0 END) as week, 111 SUM(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as month 112 FROM %i', 113 $table_name 114 ) 110 115 ); 111 116 … … 280 285 <table class="form-table"> 281 286 <tr> 282 <th scope="row"><?php _e( 'Comment Protection', 'spamjam' ); ?></th>287 <th scope="row"><?php esc_html_e( 'Comment Protection', 'spamjam' ); ?></th> 283 288 <td> 284 289 <label class="switch"> … … 287 292 <span class="slider round"></span> 288 293 </label> 289 <p class="description"><?php _e( 'Enable spam protection for comments (always enabled)', 'spamjam' ); ?></p>294 <p class="description"><?php esc_html_e( 'Enable spam protection for comments (always enabled)', 'spamjam' ); ?></p> 290 295 </td> 291 296 </tr> … … 293 298 <tr> 294 299 <th scope="row"> 295 <?php _e( 'Registration Protection', 'spamjam' ); ?>300 <?php esc_html_e( 'Registration Protection', 'spamjam' ); ?> 296 301 <span class="plan-badge pro">PRO</span> 297 302 </th> … … 300 305 <input type="checkbox" name="spamjam_enable_registration_protection" value="1" 301 306 <?php checked( 1, get_option( 'spamjam_enable_registration_protection', 0 ) ); ?> 302 <?php echo $disabled; ?>>303 <span class="slider round"></span> 304 </label> 305 <p class="description"><?php _e( 'Protect registration forms from spam bots', 'spamjam' ); ?></p>307 <?php echo esc_attr( $disabled ); ?>> 308 <span class="slider round"></span> 309 </label> 310 <p class="description"><?php esc_html_e( 'Protect registration forms from spam bots', 'spamjam' ); ?></p> 306 311 </td> 307 312 </tr> … … 309 314 <tr> 310 315 <th scope="row"> 311 <?php _e( 'Contact Form Protection', 'spamjam' ); ?>316 <?php esc_html_e( 'Contact Form Protection', 'spamjam' ); ?> 312 317 <span class="plan-badge business">BUSINESS</span> 313 318 </th> … … 316 321 <input type="checkbox" name="spamjam_enable_contact_form_protection" value="1" 317 322 <?php checked( 1, get_option( 'spamjam_enable_contact_form_protection', 0 ) ); ?> 318 <?php echo $disabled; ?>>319 <span class="slider round"></span> 320 </label> 321 <p class="description"><?php _e( 'Protect Contact Form 7, WPForms, and Gravity Forms', 'spamjam' ); ?></p>323 <?php echo esc_attr( $disabled ); ?>> 324 <span class="slider round"></span> 325 </label> 326 <p class="description"><?php esc_html_e( 'Protect Contact Form 7, WPForms, and Gravity Forms', 'spamjam' ); ?></p> 322 327 </td> 323 328 </tr> … … 332 337 <table class="form-table"> 333 338 <tr> 334 <th scope="row"><?php _e( 'Honeypot Field Name', 'spamjam' ); ?></th>339 <th scope="row"><?php esc_html_e( 'Honeypot Field Name', 'spamjam' ); ?></th> 335 340 <td> 336 341 <input type="text" name="spamjam_honeypot_field" 337 342 value="<?php echo esc_attr( get_option( 'spamjam_honeypot_field', 'website_url' ) ); ?>" 338 343 class="regular-text"> 339 <p class="description"><?php _e( 'Custom name for the honeypot field (advanced users only)', 'spamjam' ); ?></p>344 <p class="description"><?php esc_html_e( 'Custom name for the honeypot field (advanced users only)', 'spamjam' ); ?></p> 340 345 </td> 341 346 </tr> … … 365 370 <table class="form-table"> 366 371 <tr> 367 <th scope="row"><?php _e( 'Use Premium Blocklist', 'spamjam' ); ?></th>372 <th scope="row"><?php esc_html_e( 'Use Premium Blocklist', 'spamjam' ); ?></th> 368 373 <td> 369 374 <label class="switch"> 370 375 <input type="checkbox" name="spamjam_use_premium_blocklist" value="1" 371 376 <?php checked( 1, get_option( 'spamjam_use_premium_blocklist', 0 ) ); ?> 372 <?php echo $disabled; ?>>373 <span class="slider round"></span> 374 </label> 375 <p class="description"><?php _e( 'Enable the premium blocklist with 15,000+ spam terms from WordPress Comment Blocklist', 'spamjam' ); ?></p>377 <?php echo esc_attr( $disabled ); ?>> 378 <span class="slider round"></span> 379 </label> 380 <p class="description"><?php esc_html_e( 'Enable the premium blocklist with 15,000+ spam terms from WordPress Comment Blocklist', 'spamjam' ); ?></p> 376 381 </td> 377 382 </tr> 378 383 <tr class="dependent-field" data-depends="spamjam_use_premium_blocklist"> 379 <th scope="row"><?php _e( 'Auto-Update Blocklist', 'spamjam' ); ?></th>384 <th scope="row"><?php esc_html_e( 'Auto-Update Blocklist', 'spamjam' ); ?></th> 380 385 <td> 381 386 <label class="switch"> 382 387 <input type="checkbox" name="spamjam_update_blocklist_automatically" value="1" 383 388 <?php checked( 1, get_option( 'spamjam_update_blocklist_automatically', 1 ) ); ?> 384 <?php echo $disabled; ?>>385 <span class="slider round"></span> 386 </label> 387 <p class="description"><?php _e( 'Automatically update the premium blocklist daily from GitHub', 'spamjam' ); ?></p>389 <?php echo esc_attr( $disabled ); ?>> 390 <span class="slider round"></span> 391 </label> 392 <p class="description"><?php esc_html_e( 'Automatically update the premium blocklist daily from GitHub', 'spamjam' ); ?></p> 388 393 </td> 389 394 </tr> … … 400 405 <table class="form-table"> 401 406 <tr> 402 <th scope="row"><?php _e( 'Enable Custom Blocklist', 'spamjam' ); ?></th>407 <th scope="row"><?php esc_html_e( 'Enable Custom Blocklist', 'spamjam' ); ?></th> 403 408 <td> 404 409 <label class="switch"> 405 410 <input type="checkbox" name="spamjam_enable_custom_blocklist" value="1" 406 411 <?php checked( 1, get_option( 'spamjam_enable_custom_blocklist', 0 ) ); ?> 407 <?php echo $disabled; ?>>408 <span class="slider round"></span> 409 </label> 410 <p class="description"><?php _e( 'Add your own custom keywords to block', 'spamjam' ); ?></p>412 <?php echo esc_attr( $disabled ); ?>> 413 <span class="slider round"></span> 414 </label> 415 <p class="description"><?php esc_html_e( 'Add your own custom keywords to block', 'spamjam' ); ?></p> 411 416 </td> 412 417 </tr> 413 418 <tr class="dependent-field" data-depends="spamjam_enable_custom_blocklist"> 414 <th scope="row"><?php _e( 'Blocked Keywords', 'spamjam' ); ?></th>419 <th scope="row"><?php esc_html_e( 'Blocked Keywords', 'spamjam' ); ?></th> 415 420 <td> 416 421 <textarea name="spamjam_custom_blocklist" rows="8" class="large-text" 417 <?php echo $disabled; ?>422 <?php echo esc_attr( $disabled ); ?> 418 423 placeholder="viagra casino cheap-pills"><?php echo esc_textarea( get_option( 'spamjam_custom_blocklist', '' ) ); ?></textarea> 419 <p class="description"><?php _e( 'One keyword per line. Case-insensitive.', 'spamjam' ); ?></p>424 <p class="description"><?php esc_html_e( 'One keyword per line. Case-insensitive.', 'spamjam' ); ?></p> 420 425 </td> 421 426 </tr> … … 432 437 <table class="form-table"> 433 438 <tr> 434 <th scope="row"><?php _e( 'Minimum Comment Length', 'spamjam' ); ?></th>439 <th scope="row"><?php esc_html_e( 'Minimum Comment Length', 'spamjam' ); ?></th> 435 440 <td> 436 441 <input type="number" name="spamjam_min_comment_length" 437 442 value="<?php echo esc_attr( get_option( 'spamjam_min_comment_length', 0 ) ); ?>" 438 min="0" max="1000" class="small-text" <?php echo $disabled; ?>>439 <p class="description"><?php _e( 'Minimum characters required (0 = disabled)', 'spamjam' ); ?></p>440 </td> 441 </tr> 442 <tr> 443 <th scope="row"><?php _e( 'Maximum Links Allowed', 'spamjam' ); ?></th>443 min="0" max="1000" class="small-text" <?php echo esc_attr( $disabled ); ?>> 444 <p class="description"><?php esc_html_e( 'Minimum characters required (0 = disabled)', 'spamjam' ); ?></p> 445 </td> 446 </tr> 447 <tr> 448 <th scope="row"><?php esc_html_e( 'Maximum Links Allowed', 'spamjam' ); ?></th> 444 449 <td> 445 450 <input type="number" name="spamjam_max_links_allowed" 446 451 value="<?php echo esc_attr( get_option( 'spamjam_max_links_allowed', 2 ) ); ?>" 447 min="0" max="20" class="small-text" <?php echo $disabled; ?>>448 <p class="description"><?php _e( 'Maximum number of links allowed in comments', 'spamjam' ); ?></p>452 min="0" max="20" class="small-text" <?php echo esc_attr( $disabled ); ?>> 453 <p class="description"><?php esc_html_e( 'Maximum number of links allowed in comments', 'spamjam' ); ?></p> 449 454 </td> 450 455 </tr> … … 461 466 <table class="form-table"> 462 467 <tr> 463 <th scope="row"><?php _e( 'Enable Geographic Blocking', 'spamjam' ); ?></th>468 <th scope="row"><?php esc_html_e( 'Enable Geographic Blocking', 'spamjam' ); ?></th> 464 469 <td> 465 470 <label class="switch"> 466 471 <input type="checkbox" name="spamjam_enable_country_blocking" value="1" 467 472 <?php checked( 1, get_option( 'spamjam_enable_country_blocking', 0 ) ); ?> 468 <?php echo $disabled; ?>>469 <span class="slider round"></span> 470 </label> 471 <p class="description"><?php _e( 'Block comments from specific countries', 'spamjam' ); ?></p>473 <?php echo esc_attr( $disabled ); ?>> 474 <span class="slider round"></span> 475 </label> 476 <p class="description"><?php esc_html_e( 'Block comments from specific countries', 'spamjam' ); ?></p> 472 477 </td> 473 478 </tr> 474 479 <tr class="dependent-field" data-depends="spamjam_enable_country_blocking"> 475 <th scope="row"><?php _e( 'Blocked Countries', 'spamjam' ); ?></th>480 <th scope="row"><?php esc_html_e( 'Blocked Countries', 'spamjam' ); ?></th> 476 481 <td> 477 482 <textarea name="spamjam_blocked_countries" rows="5" class="large-text" 478 <?php echo $disabled; ?>483 <?php echo esc_attr( $disabled ); ?> 479 484 placeholder="US GB CN"><?php echo esc_textarea( get_option( 'spamjam_blocked_countries', '' ) ); ?></textarea> 480 <p class="description"><?php _e( 'Two-letter country codes (ISO 3166-1 alpha-2), one per line', 'spamjam' ); ?></p>485 <p class="description"><?php esc_html_e( 'Two-letter country codes (ISO 3166-1 alpha-2), one per line', 'spamjam' ); ?></p> 481 486 </td> 482 487 </tr> … … 493 498 <table class="form-table"> 494 499 <tr> 495 <th scope="row"><?php _e( 'Enable Email Blocking', 'spamjam' ); ?></th>500 <th scope="row"><?php esc_html_e( 'Enable Email Blocking', 'spamjam' ); ?></th> 496 501 <td> 497 502 <label class="switch"> 498 503 <input type="checkbox" name="spamjam_enable_email_blocking" value="1" 499 504 <?php checked( 1, get_option( 'spamjam_enable_email_blocking', 0 ) ); ?> 500 <?php echo $disabled; ?>>501 <span class="slider round"></span> 502 </label> 503 <p class="description"><?php _e( 'Block specific email addresses or domains from commenting and registering', 'spamjam' ); ?></p>505 <?php echo esc_attr( $disabled ); ?>> 506 <span class="slider round"></span> 507 </label> 508 <p class="description"><?php esc_html_e( 'Block specific email addresses or domains from commenting and registering', 'spamjam' ); ?></p> 504 509 </td> 505 510 </tr> 506 511 <tr class="dependent-field" data-depends="spamjam_enable_email_blocking"> 507 <th scope="row"><?php _e( 'Blocked Emails', 'spamjam' ); ?></th>512 <th scope="row"><?php esc_html_e( 'Blocked Emails', 'spamjam' ); ?></th> 508 513 <td> 509 514 <textarea name="spamjam_blocked_emails" rows="8" class="large-text" 510 <?php echo $disabled; ?>515 <?php echo esc_attr( $disabled ); ?> 511 516 placeholder="[email protected] @spam-domain.com spam-domain.com"><?php echo esc_textarea( get_option( 'spamjam_blocked_emails', '' ) ); ?></textarea> 512 <p class="description"><?php _e( 'One email or domain per line. Examples: [email protected], @example.com, or example.com', 'spamjam' ); ?></p>517 <p class="description"><?php esc_html_e( 'One email or domain per line. Examples: [email protected], @example.com, or example.com', 'spamjam' ); ?></p> 513 518 </td> 514 519 </tr> … … 525 530 <table class="form-table"> 526 531 <tr> 527 <th scope="row"><?php _e( 'Enable IP Blocking', 'spamjam' ); ?></th>532 <th scope="row"><?php esc_html_e( 'Enable IP Blocking', 'spamjam' ); ?></th> 528 533 <td> 529 534 <label class="switch"> 530 535 <input type="checkbox" name="spamjam_enable_ip_blocking" value="1" 531 536 <?php checked( 1, get_option( 'spamjam_enable_ip_blocking', 0 ) ); ?> 532 <?php echo $disabled; ?>>533 <span class="slider round"></span> 534 </label> 535 <p class="description"><?php _e( 'Block comments from specific IP addresses or ranges', 'spamjam' ); ?></p>537 <?php echo esc_attr( $disabled ); ?>> 538 <span class="slider round"></span> 539 </label> 540 <p class="description"><?php esc_html_e( 'Block comments from specific IP addresses or ranges', 'spamjam' ); ?></p> 536 541 </td> 537 542 </tr> 538 543 <tr class="dependent-field" data-depends="spamjam_enable_ip_blocking"> 539 <th scope="row"><?php _e( 'Blocked IPs', 'spamjam' ); ?></th>544 <th scope="row"><?php esc_html_e( 'Blocked IPs', 'spamjam' ); ?></th> 540 545 <td> 541 546 <textarea name="spamjam_blocked_ips" rows="8" class="large-text" 542 <?php echo $disabled; ?>547 <?php echo esc_attr( $disabled ); ?> 543 548 placeholder="192.168.1.1 10.0.0.0/8"><?php echo esc_textarea( get_option( 'spamjam_blocked_ips', '' ) ); ?></textarea> 544 <p class="description"><?php _e( 'One IP address or CIDR range per line', 'spamjam' ); ?></p>549 <p class="description"><?php esc_html_e( 'One IP address or CIDR range per line', 'spamjam' ); ?></p> 545 550 </td> 546 551 </tr> … … 565 570 <h3>📧 Trusted Emails</h3> 566 571 <span class="plan-badge business">BUSINESS</span> 567 <p class="section-description"><?php _e( 'Comments from these email addresses will always be approved and bypass all spam checks.', 'spamjam' ); ?></p>568 </div> 569 570 <table class="form-table"> 571 <tr> 572 <th scope="row"><?php _e( 'Enable Email Allowlist', 'spamjam' ); ?></th>572 <p class="section-description"><?php esc_html_e( 'Comments from these email addresses will always be approved and bypass all spam checks.', 'spamjam' ); ?></p> 573 </div> 574 575 <table class="form-table"> 576 <tr> 577 <th scope="row"><?php esc_html_e( 'Enable Email Allowlist', 'spamjam' ); ?></th> 573 578 <td> 574 579 <label class="switch"> 575 580 <input type="checkbox" name="spamjam_enable_whitelist" value="1" 576 581 <?php checked( 1, get_option( 'spamjam_enable_whitelist', 0 ) ); ?> 577 <?php echo $disabled; ?>>578 <span class="slider round"></span> 579 </label> 580 <p class="description"><?php _e( 'Always allow comments from trusted email addresses', 'spamjam' ); ?></p>582 <?php echo esc_attr( $disabled ); ?>> 583 <span class="slider round"></span> 584 </label> 585 <p class="description"><?php esc_html_e( 'Always allow comments from trusted email addresses', 'spamjam' ); ?></p> 581 586 </td> 582 587 </tr> 583 588 <tr class="dependent-field" data-depends="spamjam_enable_whitelist"> 584 <th scope="row"><?php _e( 'Allowed Emails', 'spamjam' ); ?></th>589 <th scope="row"><?php esc_html_e( 'Allowed Emails', 'spamjam' ); ?></th> 585 590 <td> 586 591 <textarea name="spamjam_whitelist_emails" rows="10" class="large-text" 587 <?php echo $disabled; ?>592 <?php echo esc_attr( $disabled ); ?> 588 593 placeholder="[email protected] [email protected] [email protected]"><?php echo esc_textarea( get_option( 'spamjam_whitelist_emails', '' ) ); ?></textarea> 589 <p class="description"><?php _e( 'One email address per line. These users will bypass all spam checks.', 'spamjam' ); ?></p>594 <p class="description"><?php esc_html_e( 'One email address per line. These users will bypass all spam checks.', 'spamjam' ); ?></p> 590 595 </td> 591 596 </tr> … … 620 625 <h3>⏱️ Rate Limiting</h3> 621 626 <span class="plan-badge pro">PRO</span> 622 <p class="section-description"><?php _e( 'Prevent spam floods by limiting how often users can comment.', 'spamjam' ); ?></p>623 </div> 624 625 <table class="form-table"> 626 <tr> 627 <th scope="row"><?php _e( 'Enable Rate Limiting', 'spamjam' ); ?></th>627 <p class="section-description"><?php esc_html_e( 'Prevent spam floods by limiting how often users can comment.', 'spamjam' ); ?></p> 628 </div> 629 630 <table class="form-table"> 631 <tr> 632 <th scope="row"><?php esc_html_e( 'Enable Rate Limiting', 'spamjam' ); ?></th> 628 633 <td> 629 634 <label class="switch"> 630 635 <input type="checkbox" name="spamjam_enable_rate_limiting" value="1" 631 636 <?php checked( 1, get_option( 'spamjam_enable_rate_limiting', 0 ) ); ?> 632 <?php echo $disabled; ?>>633 <span class="slider round"></span> 634 </label> 635 <p class="description"><?php _e( 'Limit comment frequency per IP address', 'spamjam' ); ?></p>637 <?php echo esc_attr( $disabled ); ?>> 638 <span class="slider round"></span> 639 </label> 640 <p class="description"><?php esc_html_e( 'Limit comment frequency per IP address', 'spamjam' ); ?></p> 636 641 </td> 637 642 </tr> 638 643 <tr class="dependent-field" data-depends="spamjam_enable_rate_limiting"> 639 <th scope="row"><?php _e( 'Maximum Comments', 'spamjam' ); ?></th>644 <th scope="row"><?php esc_html_e( 'Maximum Comments', 'spamjam' ); ?></th> 640 645 <td> 641 646 <input type="number" name="spamjam_rate_limit_count" 642 647 value="<?php echo esc_attr( get_option( 'spamjam_rate_limit_count', 3 ) ); ?>" 643 min="1" max="100" class="small-text" <?php echo $disabled; ?>>644 <p class="description"><?php _e( 'Maximum number of comments allowed per time period', 'spamjam' ); ?></p>648 min="1" max="100" class="small-text" <?php echo esc_attr( $disabled ); ?>> 649 <p class="description"><?php esc_html_e( 'Maximum number of comments allowed per time period', 'spamjam' ); ?></p> 645 650 </td> 646 651 </tr> 647 652 <tr class="dependent-field" data-depends="spamjam_enable_rate_limiting"> 648 <th scope="row"><?php _e( 'Time Period (minutes)', 'spamjam' ); ?></th>653 <th scope="row"><?php esc_html_e( 'Time Period (minutes)', 'spamjam' ); ?></th> 649 654 <td> 650 655 <input type="number" name="spamjam_rate_limit_period" 651 656 value="<?php echo esc_attr( get_option( 'spamjam_rate_limit_period', 10 ) ); ?>" 652 min="1" max="1440" class="small-text" <?php echo $disabled; ?>>653 <p class="description"><?php _e( 'Time window for rate limiting (1-1440 minutes)', 'spamjam' ); ?></p>657 min="1" max="1440" class="small-text" <?php echo esc_attr( $disabled ); ?>> 658 <p class="description"><?php esc_html_e( 'Time window for rate limiting (1-1440 minutes)', 'spamjam' ); ?></p> 654 659 </td> 655 660 </tr> … … 681 686 <h3>📝 Spam Logger</h3> 682 687 <span class="plan-badge business">BUSINESS</span> 683 <p class="section-description"><?php _e( 'Keep a detailed log of all blocked spam attempts.', 'spamjam' ); ?></p>684 </div> 685 686 <table class="form-table"> 687 <tr> 688 <th scope="row"><?php _e( 'Enable Spam Logging', 'spamjam' ); ?></th>688 <p class="section-description"><?php esc_html_e( 'Keep a detailed log of all blocked spam attempts.', 'spamjam' ); ?></p> 689 </div> 690 691 <table class="form-table"> 692 <tr> 693 <th scope="row"><?php esc_html_e( 'Enable Spam Logging', 'spamjam' ); ?></th> 689 694 <td> 690 695 <label class="switch"> 691 696 <input type="checkbox" name="spamjam_enable_spam_logging" value="1" 692 697 <?php checked( 1, get_option( 'spamjam_enable_spam_logging', 0 ) ); ?> 693 <?php echo $disabled; ?>>694 <span class="slider round"></span> 695 </label> 696 <p class="description"><?php _e( 'Log all blocked spam attempts to database', 'spamjam' ); ?></p>698 <?php echo esc_attr( $disabled ); ?>> 699 <span class="slider round"></span> 700 </label> 701 <p class="description"><?php esc_html_e( 'Log all blocked spam attempts to database', 'spamjam' ); ?></p> 697 702 </td> 698 703 </tr> 699 704 <tr class="dependent-field" data-depends="spamjam_enable_spam_logging"> 700 <th scope="row"><?php _e( 'Log Retention (days)', 'spamjam' ); ?></th>705 <th scope="row"><?php esc_html_e( 'Log Retention (days)', 'spamjam' ); ?></th> 701 706 <td> 702 707 <input type="number" name="spamjam_log_retention_days" 703 708 value="<?php echo esc_attr( get_option( 'spamjam_log_retention_days', 30 ) ); ?>" 704 min="1" max="365" class="small-text" <?php echo $disabled; ?>>705 <p class="description"><?php _e( 'How long to keep logs (1-365 days)', 'spamjam' ); ?></p>709 min="1" max="365" class="small-text" <?php echo esc_attr( $disabled ); ?>> 710 <p class="description"><?php esc_html_e( 'How long to keep logs (1-365 days)', 'spamjam' ); ?></p> 706 711 </td> 707 712 </tr> … … 730 735 $table_name = $wpdb->prefix . 'spamjam_log'; 731 736 732 if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) !== $table_name ) { 733 echo '<p>' . __( 'Spam log table not found. Logs will be created when spam is blocked.', 'spamjam' ) . '</p>'; 737 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 738 if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) !== $table_name ) { 739 echo '<p>' . esc_html__( 'Spam log table not found. Logs will be created when spam is blocked.', 'spamjam' ) . '</p>'; 734 740 return; 735 741 } … … 741 747 if ( false === $logs ) { 742 748 // Cache miss - query database. 749 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 743 750 $logs = $wpdb->get_results( 744 "SELECT id, ip_address, email, reason, content, blocked_at 745 FROM $table_name 746 ORDER BY blocked_at DESC 747 LIMIT 50" 751 $wpdb->prepare( 752 'SELECT id, ip_address, email, reason, content, blocked_at 753 FROM %i 754 ORDER BY blocked_at DESC 755 LIMIT 50', 756 $table_name 757 ) 748 758 ); 749 759 // Cache for 5 minutes. … … 759 769 <thead> 760 770 <tr> 761 <th><?php _e( 'Date', 'spamjam' ); ?></th>762 <th><?php _e( 'IP Address', 'spamjam' ); ?></th>763 <th><?php _e( 'Email', 'spamjam' ); ?></th>764 <th><?php _e( 'Reason', 'spamjam' ); ?></th>765 <th><?php _e( 'Content Preview', 'spamjam' ); ?></th>771 <th><?php esc_html_e( 'Date', 'spamjam' ); ?></th> 772 <th><?php esc_html_e( 'IP Address', 'spamjam' ); ?></th> 773 <th><?php esc_html_e( 'Email', 'spamjam' ); ?></th> 774 <th><?php esc_html_e( 'Reason', 'spamjam' ); ?></th> 775 <th><?php esc_html_e( 'Content Preview', 'spamjam' ); ?></th> 766 776 </tr> 767 777 </thead> … … 802 812 <h3>🔌 API Access</h3> 803 813 <span class="plan-badge agency">AGENCY</span> 804 <p class="section-description"><?php _e( 'Allow external applications to access spam statistics and logs via REST API.', 'spamjam' ); ?></p>805 </div> 806 807 <table class="form-table"> 808 <tr> 809 <th scope="row"><?php _e( 'Enable API Access', 'spamjam' ); ?></th>814 <p class="section-description"><?php esc_html_e( 'Allow external applications to access spam statistics and logs via REST API.', 'spamjam' ); ?></p> 815 </div> 816 817 <table class="form-table"> 818 <tr> 819 <th scope="row"><?php esc_html_e( 'Enable API Access', 'spamjam' ); ?></th> 810 820 <td> 811 821 <label class="switch"> 812 822 <input type="checkbox" name="spamjam_enable_api_access" value="1" 813 823 <?php checked( 1, get_option( 'spamjam_enable_api_access', 0 ) ); ?> 814 <?php echo $disabled; ?>>815 <span class="slider round"></span> 816 </label> 817 <p class="description"><?php _e( 'Enable REST API endpoints for external access', 'spamjam' ); ?></p>824 <?php echo esc_attr( $disabled ); ?>> 825 <span class="slider round"></span> 826 </label> 827 <p class="description"><?php esc_html_e( 'Enable REST API endpoints for external access', 'spamjam' ); ?></p> 818 828 </td> 819 829 </tr> 820 830 <tr class="dependent-field" data-depends="spamjam_enable_api_access"> 821 <th scope="row"><?php _e( 'API Key', 'spamjam' ); ?></th>831 <th scope="row"><?php esc_html_e( 'API Key', 'spamjam' ); ?></th> 822 832 <td> 823 833 <input type="text" name="spamjam_api_key" readonly 824 834 value="<?php echo esc_attr( $api_key ); ?>" 825 835 class="regular-text" style="font-family: monospace;"> 826 <p class="description"><?php _e( 'Use this key in the Authorization header: Bearer YOUR_API_KEY', 'spamjam' ); ?></p>836 <p class="description"><?php esc_html_e( 'Use this key in the Authorization header: Bearer YOUR_API_KEY', 'spamjam' ); ?></p> 827 837 </td> 828 838 </tr> … … 835 845 <h3>🎨 White Label Mode</h3> 836 846 <span class="plan-badge agency">AGENCY</span> 837 <p class="section-description"><?php _e( 'Customize the plugin branding for your clients.', 'spamjam' ); ?></p>838 </div> 839 840 <table class="form-table"> 841 <tr> 842 <th scope="row"><?php _e( 'Enable White Label', 'spamjam' ); ?></th>847 <p class="section-description"><?php esc_html_e( 'Customize the plugin branding for your clients.', 'spamjam' ); ?></p> 848 </div> 849 850 <table class="form-table"> 851 <tr> 852 <th scope="row"><?php esc_html_e( 'Enable White Label', 'spamjam' ); ?></th> 843 853 <td> 844 854 <label class="switch"> 845 855 <input type="checkbox" name="spamjam_enable_white_label" value="1" 846 856 <?php checked( 1, get_option( 'spamjam_enable_white_label', 0 ) ); ?> 847 <?php echo $disabled; ?>>848 <span class="slider round"></span> 849 </label> 850 <p class="description"><?php _e( 'Remove SpamJam branding from admin interface', 'spamjam' ); ?></p>857 <?php echo esc_attr( $disabled ); ?>> 858 <span class="slider round"></span> 859 </label> 860 <p class="description"><?php esc_html_e( 'Remove SpamJam branding from admin interface', 'spamjam' ); ?></p> 851 861 </td> 852 862 </tr> 853 863 <tr class="dependent-field" data-depends="spamjam_enable_white_label"> 854 <th scope="row"><?php _e( 'Custom Plugin Name', 'spamjam' ); ?></th>864 <th scope="row"><?php esc_html_e( 'Custom Plugin Name', 'spamjam' ); ?></th> 855 865 <td> 856 866 <input type="text" name="spamjam_custom_plugin_name" 857 867 value="<?php echo esc_attr( get_option( 'spamjam_custom_plugin_name', 'SpamJam' ) ); ?>" 858 class="regular-text" <?php echo $disabled; ?>>859 <p class="description"><?php _e( 'Display name for the plugin (3-50 characters)', 'spamjam' ); ?></p>868 class="regular-text" <?php echo esc_attr( $disabled ); ?>> 869 <p class="description"><?php esc_html_e( 'Display name for the plugin (3-50 characters)', 'spamjam' ); ?></p> 860 870 </td> 861 871 </tr> … … 868 878 <h3>📊 Advanced Reporting</h3> 869 879 <span class="plan-badge agency">AGENCY</span> 870 <p class="section-description"><?php _e( 'Generate detailed reports and receive email summaries.', 'spamjam' ); ?></p>871 </div> 872 873 <table class="form-table"> 874 <tr> 875 <th scope="row"><?php _e( 'Enable Advanced Reporting', 'spamjam' ); ?></th>880 <p class="section-description"><?php esc_html_e( 'Generate detailed reports and receive email summaries.', 'spamjam' ); ?></p> 881 </div> 882 883 <table class="form-table"> 884 <tr> 885 <th scope="row"><?php esc_html_e( 'Enable Advanced Reporting', 'spamjam' ); ?></th> 876 886 <td> 877 887 <label class="switch"> 878 888 <input type="checkbox" name="spamjam_enable_advanced_reporting" value="1" 879 889 <?php checked( 1, get_option( 'spamjam_enable_advanced_reporting', 0 ) ); ?> 880 <?php echo $disabled; ?>>881 <span class="slider round"></span> 882 </label> 883 <p class="description"><?php _e( 'Generate PDF reports and email summaries', 'spamjam' ); ?></p>890 <?php echo esc_attr( $disabled ); ?>> 891 <span class="slider round"></span> 892 </label> 893 <p class="description"><?php esc_html_e( 'Generate PDF reports and email summaries', 'spamjam' ); ?></p> 884 894 </td> 885 895 </tr> 886 896 <tr class="dependent-field" data-depends="spamjam_enable_advanced_reporting"> 887 <th scope="row"><?php _e( 'Report Email', 'spamjam' ); ?></th>897 <th scope="row"><?php esc_html_e( 'Report Email', 'spamjam' ); ?></th> 888 898 <td> 889 899 <input type="email" name="spamjam_report_email" 890 900 value="<?php echo esc_attr( get_option( 'spamjam_report_email', '' ) ); ?>" 891 class="regular-text" <?php echo $disabled; ?>901 class="regular-text" <?php echo esc_attr( $disabled ); ?> 892 902 placeholder="[email protected]"> 893 <p class="description"><?php _e( 'Receive weekly spam reports at this email address', 'spamjam' ); ?></p>903 <p class="description"><?php esc_html_e( 'Receive weekly spam reports at this email address', 'spamjam' ); ?></p> 894 904 </td> 895 905 </tr> … … 902 912 <h3>🌐 Multisite Sync</h3> 903 913 <span class="plan-badge agency">AGENCY</span> 904 <p class="section-description"><?php _e( 'Synchronize settings across WordPress multisite network.', 'spamjam' ); ?></p>905 </div> 906 907 <table class="form-table"> 908 <tr> 909 <th scope="row"><?php _e( 'Enable Multisite Sync', 'spamjam' ); ?></th>914 <p class="section-description"><?php esc_html_e( 'Synchronize settings across WordPress multisite network.', 'spamjam' ); ?></p> 915 </div> 916 917 <table class="form-table"> 918 <tr> 919 <th scope="row"><?php esc_html_e( 'Enable Multisite Sync', 'spamjam' ); ?></th> 910 920 <td> 911 921 <label class="switch"> 912 922 <input type="checkbox" name="spamjam_enable_multisite_sync" value="1" 913 923 <?php checked( 1, get_option( 'spamjam_enable_multisite_sync', 0 ) ); ?> 914 <?php echo $disabled; ?>>915 <span class="slider round"></span> 916 </label> 917 <p class="description"><?php _e( 'Sync blocklists and settings across all sites in network', 'spamjam' ); ?></p>924 <?php echo esc_attr( $disabled ); ?>> 925 <span class="slider round"></span> 926 </label> 927 <p class="description"><?php esc_html_e( 'Sync blocklists and settings across all sites in network', 'spamjam' ); ?></p> 918 928 </td> 919 929 </tr> -
spamjam/trunk/includes/admin.php
r3427664 r3458129 115 115 // Check if premium features are disabled. 116 116 $disabled = 'disabled'; 117 $is_local = in_array( $_SERVER['HTTP_HOST'] ?? '', [ 'localhost', '127.0.0.1', 'utopiqueplugins.local' ], true );117 $is_local = in_array( wp_parse_url( home_url(), PHP_URL_HOST ), [ 'localhost', '127.0.0.1', 'utopiqueplugins.local' ], true ); 118 118 119 119 if ( $is_local || ( spamjam_fs()->is__premium_only() && spamjam_fs()->can_use_premium_code() && spamjam_fs()->is_plan__premium_only( 'pro' ) ) ) { -
spamjam/trunk/includes/migration.php
r3427664 r3458129 86 86 } 87 87 88 // Initialize WP_Filesystem. 89 global $wp_filesystem; 90 if ( ! function_exists( 'WP_Filesystem' ) ) { 91 require_once ABSPATH . 'wp-admin/includes/file.php'; 92 } 93 WP_Filesystem(); 94 95 if ( ! $wp_filesystem ) { 96 return; 97 } 98 88 99 // Read legacy file. 89 $legacy_content = file_get_contents( $blocklist_file_path );100 $legacy_content = $wp_filesystem->get_contents( $blocklist_file_path ); 90 101 91 102 if ( false === $legacy_content || empty( $legacy_content ) ) { … … 95 106 // Migrate to new option. 96 107 update_option( 'spamjam_premium_blocklist_data', $legacy_content ); 97 update_option( 'spamjam_premium_blocklist_last_updated', filemtime( $blocklist_file_path ) ); 108 $file_mtime = $wp_filesystem->mtime( $blocklist_file_path ); 109 update_option( 'spamjam_premium_blocklist_last_updated', $file_mtime ); 98 110 99 111 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { … … 101 113 } 102 114 103 // Optionally rename the old file(don't delete in case of issues).115 // Optionally move the old file to a backup (don't delete in case of issues). 104 116 $backup_path = $blocklist_file_path . '.backup'; 105 if ( ! file_exists( $backup_path ) ) {106 rename( $blocklist_file_path, $backup_path );117 if ( ! $wp_filesystem->exists( $backup_path ) ) { 118 $wp_filesystem->move( $blocklist_file_path, $backup_path ); 107 119 } 108 120 } … … 118 130 119 131 if ( file_exists( $backup_path ) ) { 120 unlink( $backup_path );132 wp_delete_file( $backup_path ); 121 133 122 134 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { -
spamjam/trunk/includes/settings-schema.php
r3427664 r3458129 435 435 } 436 436 // Dev mode - always return agency for local development. 437 $is_local = in_array( $_SERVER['HTTP_HOST'] ?? '', ['localhost', '127.0.0.1', 'utopiqueplugins.local'], true );437 $is_local = in_array( wp_parse_url( home_url(), PHP_URL_HOST ), ['localhost', '127.0.0.1', 'utopiqueplugins.local'], true ); 438 438 if ( $is_local ) { 439 439 $plan = 'agency'; -
spamjam/trunk/readme.txt
r3428403 r3458129 6 6 Tested up to: 6.9 7 7 Requires PHP: 7.4 8 Stable tag: 1.0.7 8 Stable tag: 1.1.0 9 WC requires at least: 5.3 10 WC tested up to: 10.5 11 WP requires at least: 6.2 12 WP tested up to: 6.9 9 13 License: GPLv3 or later 10 14 … … 167 171 == Changelog == 168 172 173 = 1.1.0 - 2025-02-10 = 174 * Security - Fixed SQL injection risk: all queries now use $wpdb->prepare() 175 * Security - Fixed email confirmation auth flaw with persistent hashed token 176 * Security - Fixed premium plan bypass via HTTP Host header spoofing 177 * Security - Sanitized all $_SERVER['REMOTE_ADDR'] via centralized helper 178 * Security - Added wp_unslash() to all raw $_POST and nonce verifications 179 * Security - Escaped all admin notice output in multisite sync 180 * WP VIP - Replaced file_get_contents/rename/unlink with WP_Filesystem API 181 * WPCS - Replaced all _e() with esc_html_e() in admin templates 182 * WPCS - Escaped $disabled output with esc_attr() 183 * WPCS - Replaced date() with gmdate(), deprecated current_time('timestamp') with time() 184 * Enhancement - Added WooCommerce product_instance_caching and cart_checkout_blocks compatibility 185 169 186 = 1.0.7 - 2025-12-27 = 170 187 * Fixed - Field swap hook changed from pre_comment_on_post to init for better compatibility … … 188 205 == Upgrade Notice == 189 206 190 = 1. 0.3=191 Major milestone release! Performance improvements, PHP 7.4+ requirement, updated Freemius SDK, and proper cleanup on deactivation. Recommended update for all users.207 = 1.1.0 = 208 Security hardening release. Fixes SQL injection vectors, email confirmation auth flaw, premium bypass vulnerability, and unsanitized input handling. All users should update immediately. -
spamjam/trunk/spamjam.php
r3428403 r3458129 5 5 * Plugin URI: https://utopique.net/products/spamjam/ 6 6 * Description: SpamJam silently kills spam in comments and registration. 7 * Version: 1. 0.77 * Version: 1.1.0 8 8 * Author: Utopique 9 9 * Author URI: https://utopique.net/ 10 * Copyright: 2022-202 5Utopique10 * Copyright: 2022-2026 Utopique 11 11 * Text Domain: spamjam 12 12 * License: GPLv2 or later … … 15 15 * Requires PHP: 7.4 16 16 * WC requires at least: 5.3 17 * WC tested up to: 10.4 17 * WC tested up to: 10.5 18 * WP requires at least: 6.2 19 * WP tested up to: 6.9 18 20 * PHP version 7.4+ 19 21 * … … 85 87 add_action( 'plugins_loaded', __NAMESPACE__ . '\\load_textdomain' ); 86 88 /** 89 * Get the sanitized and validated client IP address. 90 * 91 * @return string Validated IP address or empty string if invalid. 92 */ 93 function get_client_ip() { 94 $ip = ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '' ); 95 if ( !empty( $ip ) && !filter_var( $ip, FILTER_VALIDATE_IP ) ) { 96 return ''; 97 } 98 return $ip; 99 } 100 101 /** 87 102 * Block direct requests to wp-comments-post.php by checking the referrer URL. 88 103 * … … 262 277 */ 263 278 // Honeypot technique: check if a hidden field has been filled out. 264 if ( !empty( $_POST['email_confirm'] ) ) { 279 if ( !empty( wp_unslash( $_POST['email_confirm'] ) ) ) { 280 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- only checking emptiness 265 281 $error_messages[] = __( 'Honeypot field should be empty.', 'spamjam' ); 266 282 } … … 322 338 // Only run on comment submission. 323 339 if ( !isset( $_POST['comment_post_ID'] ) ) { 340 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- only checking existence 324 341 return; 325 342 } 326 343 // Debug: Log what we're receiving (only if WP_DEBUG is enabled). 327 344 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 328 error_log( 'SpamJam Field Swap - POST comment: ' . (( isset( $_POST['comment'] ) ? $_POST['comment']: 'NOT SET' )) );329 error_log( 'SpamJam Field Swap - POST sj-comment: ' . (( isset( $_POST['sj-comment'] ) ? $_POST['sj-comment']: 'NOT SET' )) );345 error_log( 'SpamJam Field Swap - POST comment: ' . (( isset( $_POST['comment'] ) ? sanitize_text_field( wp_unslash( $_POST['comment'] ) ) : 'NOT SET' )) ); 346 error_log( 'SpamJam Field Swap - POST sj-comment: ' . (( isset( $_POST['sj-comment'] ) ? sanitize_text_field( wp_unslash( $_POST['sj-comment'] ) ) : 'NOT SET' )) ); 330 347 error_log( 'SpamJam Field Swap - POST nonce: ' . (( isset( $_POST['spamjam_comment_nonce'] ) ? 'SET' : 'NOT SET' )) ); 331 348 } … … 360 377 if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { 361 378 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); 379 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'product_instance_caching', __FILE__, true ); 380 \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', __FILE__, true ); 362 381 } 363 382 } ); -
spamjam/trunk/vendor/composer/installed.php
r3427664 r3458129 4 4 'pretty_version' => 'dev-master', 5 5 'version' => 'dev-master', 6 'reference' => ' a307848b132e037ca2717f4ca7f613e434109820',6 'reference' => 'c5c12c0aeffacbb8305b8b6e546975f783150dd5', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-master', 15 15 'version' => 'dev-master', 16 'reference' => ' a307848b132e037ca2717f4ca7f613e434109820',16 'reference' => 'c5c12c0aeffacbb8305b8b6e546975f783150dd5', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../',
Note: See TracChangeset
for help on using the changeset viewer.