fix: comprehensive code quality remediation from audit findings

Security (7 fixes):
- SEC-001: Use wpdb->prepare() for COUNT query in REST API
- SEC-002: Set httponly=true on session cookie to prevent XSS
- SEC-003: Mask API keys in admin settings (show last 4 chars only)
- SEC-004: Check capability before nonce in manual fetch handler
- SEC-005: Validate redirect domains to prevent open redirects
- SEC-006: Escape all article fields before REST API response
- SEC-007: Add capability check to AJAX summary handler

Infrastructure (7 fixes):
- WPS-002: Clear cron schedule on plugin deactivation
- WPS-003: Bundle Chart.js locally instead of CDN without SRI
- WPS-004: Add uninstall.php for clean plugin removal
- DB-001: Add foreign key constraints with CASCADE deletes
- DB-002: Clamp pagination to valid page range
- CI-001: Use StrictHostKeyChecking=accept-new in deploy
- CI-002: Add security scanning step to CI pipeline
- CI-003: Add rollback workflow via workflow_dispatch

Performance & Quality (8 fixes):
- PHP-001: Validate widget $instance as array before access
- PHP-002: Return null from get_client_ip() for invalid IPs
- PHP-003: Process RSS feeds in chunked batches of 10
- PHP-004: Wire up clear_article_cache() in fetch cycle
- PERF-001: Cache source names in transient for strip_source_suffix
- PERF-002: Add server-side transient caching for REST API
- ARCH-002: Make LLM API URL configurable via WordPress options

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Terence Ong
2026-04-08 11:47:43 +07:00
parent 44cc4f56a3
commit 32e21bc59d
30 changed files with 2109 additions and 230 deletions

View File

@@ -9,15 +9,21 @@
*/
class Peptide_News_LLM {
/** @var string OpenRouter API endpoint. */
const API_URL = 'https://openrouter.ai/api/v1/chat/completions';
/** @var int Maximum retries for rate-limited requests. */
const MAX_RETRIES = 1;
/** @var int Maximum elapsed seconds before stopping a batch. */
const BATCH_TIMEOUT = 120;
/**
* Get the OpenRouter API URL from options with a hardcoded default.
*
* @return string
*/
private static function get_api_url() {
return get_option( 'peptide_news_llm_api_url', 'https://openrouter.ai/api/v1/chat/completions' );
}
/**
* Check whether LLM processing is enabled and configured.
*
@@ -276,7 +282,7 @@ class Peptide_News_LLM {
$backoff = 2; // Initial backoff in seconds.
while ( $retries <= self::MAX_RETRIES ) {
$response = wp_remote_post( self::API_URL, array(
$response = wp_remote_post( self::get_api_url(), array(
'timeout' => 15,
'headers' => array(
'Authorization' => 'Bearer ' . $api_key,
@@ -406,9 +412,8 @@ class Peptide_News_LLM {
*/
public static function ajax_generate_summaries() {
check_ajax_referer( 'peptide_news_admin', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( 'Unauthorized', 403 );
wp_die( esc_html__( 'Unauthorized', 'peptide-news' ), 403 );
}
if ( ! self::is_enabled() ) {