REPORT / 01
Wordfence CVE Analysis
CWE-89· Year: 2025
Analyzed CVEs:CVE-2025-10587,CVE-2025-28983,CVE-2025-10586,CVE-2025-54726,CVE-2025-26988,CVE-2025-31599,CVE-2025-2010,CVE-2025-32550,CVE-2025-7670,CVE-2025-24664
02 ·
Lifecycle actions
cancel · resume · skip · regenerate
03 ·
Share this analysis
copy link · embed report
03 ·
CVE Security Analysis & Writeups
ai-generated · per cve
Comprehensive security analysis generated by AI for each confirmed CVE match. Click on a CVE to view the detailed writeup including vulnerability background, technical details, patch analysis, and PoC guide.
CVE-2025-10586
NVD
AI-Generated Analysis
CVE-2025-10587
NVD
AI-Generated Analysis
CVE-2025-2010
NVD
AI-Generated Analysis
CVE-2025-26988
NVD
AI-Generated Analysis
CVE-2025-28983
NVD
AI-Generated Analysis
CVE-2025-31599
NVD
AI-Generated Analysis
CVE-2025-32550
NVD
AI-Generated Analysis
CVE-2025-54726
NVD
AI-Generated Analysis
CVE-2025-7670
NVD
AI-Generated Analysis
05 ·
Findings
filter · search · paginate
Showing 0 to 0 of 0 results
[CVE-2025-32550] [Click & Pledge CONNECT: 2.24080000-WP6.6.1→2.24120000-WP6.7.1] cnpPledgeTVDetails.php
AI: 3 vulnerabilities
3 true positives
CVE-2025-32550
--- cache/click-pledge-connect_2.24080000-WP6.6.1/cnpPledgeTVDetails.php 2025-12-04 14:06:23.600345305 +0000+++ cache/click-pledge-connect_2.24120000-WP6.7.1/cnpPledgeTVDetails.php 2025-12-04 14:06:27.808615423 +0000@@ -1,70 +1,90 @@-<?php -function cnp_pledgetvformdetails() { - - global $wpdb; global $cnp_settingtable_name;global $cnp_table_name; - $info = $_REQUEST["info"]; - $cnpresltdsply = ""; - if($info=="saved") - { - echo "<div class='updated' id='message'><p><strong>Form Added</strong>.</p></div>"; - } - if($info=="failed") - { - echo "<div class='updated' id='message'><p><strong>Already Existed</strong>.</p></div>"; - } - if($info=="upd") - { - echo "<div class='updated' id='message'><p><strong>Form updated</strong>.</p></div>"; - } - if($info=="sts") - { - echo "<div class='updated' id='message'><p><strong>Status updated</strong>.</p></div>"; - } - if($info=="del") - { - $delid=$_GET["did"]; - $wpdb->query("delete from ".$cnp_table_name." where cnpform_ID =".$delid); - echo "<div class='updated' id='message'><p><strong>Record Deleted.</strong>.</p></div>"; - } - if(isset($_GET['cnpsts']) && $_GET['cnpsts'] !="") - { - $cnpstsrtnval = CNPCF_updateCnPstatus($cnp_table_name,'cnpform_status','cnpform_ID',$_GET['cnpviewid'],$_GET['cnpsts']); - if($cnpstsrtnval == true){$cnpredirectval = "sts";}else{$cnpredirectval = "stsfail";} - wp_redirect("admin.php?page=cnp_formsdetails&info=".$cnpredirectval); - exit; - } - -?> -<script type="text/javascript"> - /* <![CDATA[ */ - jQuery(document).ready(function(){ - jQuery('#cnpformslist').dataTable(); - jQuery("tr:even").css("background-color", "#f1f1f1"); - }); - /* ]]> */ - -</script> -<?php - $cnpresltdsply = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><div class="wrap"> - <h2>Latest Channels</h2><p></p> - <table class="wp-list-table widefat " id="cnpformslist" ><thead><tr><th><u>Channel ID</u></th><th><u>Channel Name</u></th><th><u>Raised</u></th><th><u>Plays</u></th></tr></thead><tbody>'; - - - //$cnpresltdsply .= '<tr><td></td><td></td><td ></td><td></td></tr>'; - $cnpresltdsply .= '<tr><td colspan=4>No Record Found!</td></tr>'; - - $cnpresltdsply .= '</tbody></table></div>'; - echo $cnpresltdsply ; - - $cnpresltdsply = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><div class="wrap"> - <h2>Latest Pledge Videos</h2><p></p> - <table class="wp-list-table widefat " id="cnpformslist" ><thead><tr><th><u>Name</u></th><th><u>Raised</u></th><th><u>Plays</u></th></tr></thead><tbody>'; - - // $cnpresltdsply .= '<tr><td colspan=4></td><td></td><td ></td></tr>'; - $cnpresltdsply .= '<tr><td colspan=3>No Record Found!</td></tr>'; - - - $cnpresltdsply .= '</tbody></table></div>'; - echo $cnpresltdsply ; -} +<?php+function cnp_pledgetvformdetails() {++ global $wpdb; global $cnp_settingtable_name;global $cnp_table_name;+ $info = $_REQUEST["info"];+ $cnpresltdsply = "";+ if($info=="saved")+ {+ echo "<div class='updated' id='message'><p><strong>Form Added</strong>.</p></div>";+ }+ if($info=="failed")+ {+ echo "<div class='updated' id='message'><p><strong>Already Existed</strong>.</p></div>";+ }+ if($info=="upd")+ {+ echo "<div class='updated' id='message'><p><strong>Form updated</strong>.</p></div>";+ }+ if($info=="sts")+ {+ echo "<div class='updated' id='message'><p><strong>Status updated</strong>.</p></div>";+ }+ if ($info == "del") {+ + $delid = isset($_GET['did']) ? intval($_GET['did']) : 0;++ if ($delid > 0) {+ + $wpdb->query($wpdb->prepare("DELETE FROM $cnp_table_name WHERE cnpform_ID = %d", $delid));+ + echo "<div class='updated' id='message'><p><strong>Record Deleted.</strong></p></div>";+ } else {+ echo "<div class='error' id='message'><p><strong>Invalid ID.</strong></p></div>";+ }+}++if (isset($_GET['cnpsts']) && $_GET['cnpsts'] != "") {+ + $cnpsts = sanitize_text_field($_GET['cnpsts']);+ $cnpviewid = isset($_GET['cnpviewid']) ? intval($_GET['cnpviewid']) : 0;++ if ($cnpviewid > 0 && in_array($cnpsts, ['active', 'inactive'], true)) { + + $cnpstsrtnval = CNPCF_updateCnPstatus($cnp_table_name, 'cnpform_status', 'cnpform_ID', $cnpviewid, $cnpsts);+ + if ($cnpstsrtnval) {+ $cnpredirectval = "sts";+ } else {+ $cnpredirectval = "stsfail";+ }++ wp_redirect("admin.php?page=cnp_formsdetails&info=" . $cnpredirectval);+ exit;+ } else {+ echo "<div class='error' id='message'><p><strong>Invalid status or ID.</strong></p></div>";+ }+}+++?>+<script type="text/javascript">+ /* <![CDATA[ */+ jQuery(document).ready(function(){+ jQuery('#cnpformslist').dataTable();+ jQuery("tr:even").css("background-color", "#f1f1f1");+ });+ /* ]]> */++</script>+<?php+ $cnpresltdsply = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><div class="wrap">+ <h2>Latest Channels</h2><p></p>+ <table class="wp-list-table widefat " id="cnpformslist" ><thead><tr><th><u>Channel ID</u></th><th><u>Channel Name</u></th><th><u>Raised</u></th><th><u>Plays</u></th></tr></thead><tbody>';++ $cnpresltdsply .= '<tr><td colspan=4>No Record Found!</td></tr>';+ + $cnpresltdsply .= '</tbody></table></div>';+ echo $cnpresltdsply ;+ + $cnpresltdsply = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><div class="wrap">+ <h2>Latest Pledge Videos</h2><p></p>+ <table class="wp-list-table widefat " id="cnpformslist" ><thead><tr><th><u>Name</u></th><th><u>Raised</u></th><th><u>Plays</u></th></tr></thead><tbody>';++ $cnpresltdsply .= '<tr><td colspan=3>No Record Found!</td></tr>';+ + + $cnpresltdsply .= '</tbody></table></div>';+ echo $cnpresltdsply ;+} ?>\ No newline at end of file
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - cnpPledgeTVDetails.php lines 23-24
Old Code:
```php
$delid=$_GET["did"];
$wpdb->query("delete from ".$cnp_table_name." where cnpform_ID =".$delid);
```
Fixed Code:
```php
$delid = isset($_GET['did']) ? intval($_GET['did']) : 0;
if ($delid > 0) {
$wpdb->query($wpdb->prepare("DELETE FROM $cnp_table_name WHERE cnpform_ID = %d", $delid));
}
```
Explanation:
The old code directly concatenated the user-controlled `$_GET['did']` into an SQL query, enabling SQL injection if a malicious value was provided. The fix uses `intval()` to ensure the value is an integer and uses `$wpdb->prepare()` with a parameterized query, effectively neutralizing the injection risk.
Vulnerability Existed: yes
TRUE POSITIVE
CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') - cnpPledgeTVDetails.php lines 36-37
Old Code:
```php
$cnpstsrtnval = CNPCF_updateCnPstatus($cnp_table_name,'cnpform_status','cnpform_ID',$_GET['cnpviewid'],$_GET['cnpsts']);
```
Fixed Code:
```php
$cnpsts = sanitize_text_field($_GET['cnpsts']);
$cnpviewid = isset($_GET['cnpviewid']) ? intval($_GET['cnpviewid']) : 0;
if ($cnpviewid > 0 && in_array($cnpsts, ['active', 'inactive'], true)) {
$cnpstsrtnval = CNPCF_updateCnPstatus($cnp_table_name, 'cnpform_status', 'cnpform_ID', $cnpviewid, $cnpsts);
}
```
Explanation:
The old code passed unsanitized `$_GET['cnpsts']` and `$_GET['cnpviewid']` to a function and later used them in a redirect. This could allow XSS if the values were reflected in output. The fix sanitizes `cnpsts` with `sanitize_text_field()`, validates `cnpviewid` with `intval()`, and restricts `cnpsts` to specific allowed values, preventing injection.
Vulnerability Existed: yes
TRUE POSITIVE
CWE-20: Improper Input Validation - cnpPledgeTVDetails.php lines 36-37
Old Code:
```php
if(isset($_GET['cnpsts']) && $_GET['cnpsts'] !="")
{
$cnpstsrtnval = CNPCF_updateCnPstatus($cnp_table_name,'cnpform_status','cnpform_ID',$_GET['cnpviewid'],$_GET['cnpsts']);
```
Fixed Code:
```php
if (isset($_GET['cnpsts']) && $_GET['cnpsts'] != "") {
$cnpsts = sanitize_text_field($_GET['cnpsts']);
$cnpviewid = isset($_GET['cnpviewid']) ? intval($_GET['cnpviewid']) : 0;
if ($cnpviewid > 0 && in_array($cnpsts, ['active', 'inactive'], true)) {
$cnpstsrtnval = CNPCF_updateCnPstatus($cnp_table_name, 'cnpform_status', 'cnpform_ID', $cnpviewid, $cnpsts);
}
}
```
Explanation:
The old code lacked validation for `cnpviewid` and `cnpsts`, allowing arbitrary values that could lead to SQL injection, XSS, or logical errors. The fix adds type validation (`intval`), whitelisting (`in_array`), and sanitization, ensuring only expected values are processed.
CVE Analysis Results:
CVE-2025-32550: Yes
View CVE Description
Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in ClickandPledge Click & Pledge Connect Plugin allows SQL Injection. This issue affects Click & Pledge Connect Plugin: from 2.24080000 through WP6.6.1.
[CVE-2025-32550] [Click & Pledge CONNECT: 2.24080000-WP6.6.1→2.24120000-WP6.7.1] cnpSettings.php
AI: 3 vulnerabilities
3 true positives
CVE-2025-32550
--- cache/click-pledge-connect_2.24080000-WP6.6.1/cnpSettings.php 2025-12-04 14:06:23.600345305 +0000+++ cache/click-pledge-connect_2.24120000-WP6.7.1/cnpSettings.php 2025-12-04 14:06:27.808615423 +0000@@ -1,200 +1,235 @@-<?php -function cnp_formssettings() { - - global $wpdb; global $cnp_settingtable_name; - $info = $_REQUEST["info"]; - $cnpresltdsply = "";$hidval = 1;$cnpbtn = "Save"; - - - - if($info=="saved") - { - echo "<div class='updated' id='message'><p><strong>Account Added</strong>.</p></div>"; - } - if($info=="failed") - { - echo "<div class='updated' id='message'><p><strong>Please check the account details is correct or not (or) with this account id campaigns are not added.</strong>.</p></div>"; - } - if($info=="exist") - { - echo "<div class='updated' id='message'><p><strong> Friendly Name or Account Number already exist.</strong>.</p></div>"; - } - if($info=="upd") - { - echo "<div class='updated' id='message'><p><strong>Account updated</strong>.</p></div>"; - } - - if($info=="del") - { - $delid=$_GET["did"]; - $cnpnoofforms = CNPCF_getAccountNumbersInfo($delid); - $cnpnoofchnls = CNPCF_getchnlAccountNumbersInfo($delid); - if(($cnpnoofforms==0) && ($cnpnoofchnls == 0)){ - $wpdb->query("delete from ".$cnp_settingtable_name." where cnpstngs_ID =".$delid); - echo "<div class='updated' id='message'><p><strong>Record Deleted.</strong>.</p></div>"; - } - else - { - echo "<div class='updated' id='message'><p><strong>This Account Number is associated with an existing form group (or) channel group. You must first delete the form group (or) channel group before deleting this account.</strong></p></div>"; - } - - } - if(isset($_POST["cnpbtnaddsettings"])) - { - $addform=$_POST["addformval"]; - global $wpdb; - global $cnp_table_name; - if($addform==1) - {$cnprtnval=""; - $cnprtnval = CNPCF_addSettings($cnp_table_name,$_POST); - - if($cnprtnval >= 1){$cnpredirectval = "saved";} - else if($cnprtnval == "0"){$cnpredirectval = "failed";} - else if($cnprtnval == "error"){$cnpredirectval = "exist";} - else{$cnpredirectval = "failed";} - wp_redirect("admin.php?page=cnp_formssettings&info=".$cnpredirectval); - exit; - } - else if($addform==2) - { - $cnprtnval =CNPCF_updateSettings($cnp_table_name,$_POST); - if($cnprtnval >=1){$cnpredirectval = "upd";}else{$cnpredirectval = "failed";} - wp_redirect("admin.php?page=cnp_formssettings&info=".$cnpredirectval); - exit; - } - - } - $act=$_REQUEST["cnpviewid"]; - if(isset($act) && $act!="") - { - global $wpdb; - global $cnp_settingtable_name; - - $cnpfrmdtresult = CNPCF_GetCnPGroupDetails($cnp_settingtable_name,'cnpstngs_ID',$_GET['cnpviewid']); - foreach ($cnpfrmdtresult as $cnprtnval) {} - - if (count($cnprtnval)> 0 ) - { - - - $cnpsetid = $cnprtnval->cnpstngs_ID; - $cnpsetfrndlynm = $cnprtnval->cnpstngs_frndlyname; - $cnpsetAccountNumber = $cnprtnval->cnpstngs_AccountNumber; - $cnpsetguid = $cnprtnval->cnpstngs_guid; - $cnpbtn = "Update"; - $hidval = 2; - - - } - } -?> -<script type="text/javascript"> - /* <![CDATA[ */ - jQuery(document).ready(function(){ - jQuery('#cnpsettingslist').dataTable(); - jQuery("tr:even").css("background-color", "#f1f1f1"); - }); - /* ]]> */ - -</script> -<?php - $cnpfrmtyp= wp_unslash( sanitize_text_field( $_REQUEST["act"])); - if($cnpfrmtyp == "edit"){$msgdsplycntnt = "style ='display:block'";}else{$msgdsplycntnt = "style ='display:none'";} - if($cnpfrmtyp == "edit"){ $msgdbtnsplycntnt = "style ='display:none'";}else{$msgdbtnsplycntnt = "style ='float:left;display:block'";} - - $cnpresltdsply = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><div class="wrap"> - <h2>Click & Pledge Account </h2> - <div id="col-left" style="width:48% !important; float:none;"> - <div class="col-wrap"> - <div> - <div class="form-wrap"> - <table class="form-table" id="cnpformslist" align="center" > - - <tr><td> - <div> <h2>Add New Account</h2> </div> - <form class="validate" method="post" id="addsettings" name="addsettings"> - <input type="hidden" name="cnphdnediturl" id="cnphdnediturl" value="'.CNP_CF_PLUGIN_URL.'getcnpditactivecampaigns.php"> - <input type="hidden" name="cnphdnerrurl" id="cnphdnerrurl" value="'.CNP_CF_PLUGIN_URL.'cnpSettingmsgs.php"> - <input type="hidden" name="addformval" id="addformval" value='.$hidval.'> - <input type="hidden" name="hdnfrmid" id="hdnfrmid" value="'.$cnpsetid .'"> - - <div class="form-field cnpaccountId"> - <label for="tag-name"> <b>Click & Pledge Account Number*</b></label> - <input type="text" size="10" id="txtcnpacntid" name="txtcnpacntid" value="'.$cnpsetAccountNumber.'" /> - <p style="font-size:12px;">Get your "Account Number" from Click & Pledge<br> -[CONNECT > Launcher > Settings > API Information > Account ID]</p> - <span class=cnperror id="spncnpacntid"></span> - </div> - - <div class="form-field cnpacntguid"> - <label for="tag-name"> <b>Click & Pledge Account GUID*</b></label> - <input type="text" size="20" id="txtcnpacntguid" name="txtcnpacntguid" value="'.$cnpsetguid.'" /><div class="tooltip" > - <i class="fa fa-question-circle"></i> - <span class="tooltiptext">Please collect the Account GUID from your CONNECT Portal or for More Help <a href="https://support.clickandpledge.com/s/article/how-to-locate-account-id--api-account-guid" target="_blank">click here</a></span> - </div> - <p style="font-size:12px;">Get your "Account GUID" from Click & Pledge<br> - [CONNECT > Launcher > Settings > API Information > API (PaaS / FaaS): Account GUID]</p> - </div> - <div '.$msgdbtnsplycntnt.'> - <input type="button" name="cnpbtnverifysettings" id="cnpbtnverifysettings" value="Verify" class="button button-primary"><br> - - </div> - <div class="frmaddnickdiv" '.$msgdsplycntnt.'> - <div class="form-field cnpfrmfrndlynm" > - <label for="tag-name">Nickname*</label> - <input type="text" size="20" id="txtcnpfrmfrndlynm" name="txtcnpfrmfrndlynm" value="'.$cnpsetfrndlynm.'" onkeypress="return AvoidSpace(event)"/> - <span class=cnperror id="spnfrndlynm"></span> - </div> - - <div style="float:left"> - <input type="submit" name="cnpbtnaddsettings" id="cnpbtnaddsettings" value="'.$cnpbtn.'" class="button button-primary"> - </div> -</div> - </form> - </tr></td></table> - - </div> - </div> - </div> - </div> - <div > <div style="float:left" width="100%"><span class="cnperror" id="spnverify" style="display:none"><p>Communication Error:</p> - -<p>Sorry but I am having difficulty communicating with the Click & Pledge services due to the absence of the SOAP extension in your WordPress installation. The following links may help in resolving this issue:</p> - -<p>Complete details page: <a href ="http://php.net/manual/en/book.soap.php" target="_blank">http://php.net/manual/en/book.soap.php</a></p> -<p>Installing SOAP for PHP: <a href ="http://php.net/manual/en/soap.installation.php" target="_blank">http://php.net/manual/en/soap.installation.php</a></p> -<p>Configuring after installation: <a href ="http://php.net/manual/en/soap.configuration.php" target="_blank">http://php.net/manual/en/soap.configuration.php</a></p> - -<p>You may need to contact your server administrator for installation of the SOAP extension on the server.<p></span></div> - <div class="col-wrap"> - <div> - <div class="cnp_scrollable_x"> - <table style="width:99%;" class="wp-list-table widefat" id="cnpsettingslist" ><thead><tr><th>Nickname </th><th>Account Number</th><th>GUID</th><th>Created Date/Time</th><th>Actions</th></tr></thead><tbody>'; - - $sql = "select * from ".$cnp_settingtable_name." order by cnpstngs_ID desc"; - $result = $wpdb->get_results($sql); - if($wpdb->num_rows > 0 ) - { - foreach($result as $cnpsettingsData): - - $cnpform_id = $cnpsettingsData->cnpstngs_ID; - $gname = $cnpsettingsData->cnpstngs_frndlyname; - $account = $cnpsettingsData->cnpstngs_AccountNumber; - $accountguid = $cnpsettingsData->cnpstngs_guid; - $frmmodifieddt = new DateTime($cnpsettingsData->cnpstngs_Date_Modified); - $frmmodifiddt = date_format(date_create($cnpsettingsData->cnpstngs_Date_Modified),"d-m-Y H:i:s"); - - - $cnpresltdsply .= '<tr><td>'.$gname.'</td><td >'.$account.'</td><td>'.$accountguid.'</td><td data-sort="'.strtotime($frmmodifiddt).'">'.$frmmodifieddt->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP).'</td> - - <td ><u><input type="hidden" name="hdnsetngsid'.$cnpform_id.'" id="hdnsetngsid'.$cnpform_id.'" value="'.$cnpform_id .'"> - <input type="hidden" name="hdncnpaccntid'.$cnpform_id.'" id="hdncnpaccntid'.$cnpform_id.'" value="'.$account .'"> - <input type="hidden" name="hdncnpguid'.$cnpform_id.'" id="hdncnpguid'.$cnpform_id.'" value="'.$accountguid .'"> <input type="hidden" name="hdncnptblname" id="hdncnptblname" value="'.$cnp_settingtable_name .'"><a href="#" id="myHref" onclick="return mycnpaccountId('.$cnpform_id.')"><span class="dashicons dashicons-update"></span></a></u> | <u><a href="admin.php?page=cnp_formssettings&info=del&did='.$cnpform_id.'" ><span class="dashicons dashicons-trash"></span></a></u></td></tr>'; - endforeach; - } - else {$cnpresltdsply .= '<tr><td>No Record Found!</td><tr>';} - - $cnpresltdsply .= '</tbody></table></div>'; - echo $cnpresltdsply ; -} -?> +<?php+function cnp_formssettings() {++ global $wpdb; global $cnp_settingtable_name;+ $info = isset($_REQUEST["info"]) ? sanitize_text_field($_REQUEST["info"]) : '';+ $cnpresltdsply = "";$hidval = 1;$cnpbtn = "Save";+$cnpsetid ="";$cnpsetAccountNumber="";$cnpsetguid="";$cnpsetfrndlynm="";+++ if($info=="saved")+ {+ echo "<div class='updated' id='message'><p><strong>Account Added</strong>.</p></div>";+ }+ if($info=="failed")+ {+ echo "<div class='updated' id='message'><p><strong>Please check the account details is correct or not (or) with this account id campaigns are not added.</strong>.</p></div>";+ }+ if($info=="exist")+ {+ echo "<div class='updated' id='message'><p><strong> Friendly Name or Account Number already exist.</strong>.</p></div>";+ }+ if($info=="upd")+ {+ echo "<div class='updated' id='message'><p><strong>Account updated</strong>.</p></div>";+ }++ if ($info == "del") {+ $delid = isset($_GET["did"]) ? intval($_GET["did"]) : 0;++ if ($delid > 0) {+ // Retrieve associated forms and channels info+ $cnpnoofforms = CNPCF_getAccountNumbersInfo($delid);+ $cnpnoofchnls = CNPCF_getchnlAccountNumbersInfo($delid);++ // Check if there are no associated forms or channels+ if ($cnpnoofforms == 0 && $cnpnoofchnls == 0) {+ $result = $wpdb->delete(+ $cnp_settingtable_name,+ ['cnpstngs_ID' => $delid],+ ['%d'] + );++ if ($result !== false) {+ echo "<div class='updated' id='message'><p><strong>Record Deleted.</strong></p></div>";+ } else {+ echo "<div class='error' id='message'><p><strong>Error: Unable to delete the record.</strong></p></div>";+ }+ } else {+ echo "<div class='updated' id='message'><p><strong>This Account Number is associated with an existing form group or channel group. You must first delete the form group or channel group before deleting this account.</strong></p></div>";+ }+ } else {+ echo "<div class='error' id='message'><p><strong>Error: Invalid ID provided.</strong></p></div>";+ }+}+ if (isset($_POST["cnpbtnaddsettings"])) {+ $addform = isset($_POST["addformval"]) ? intval($_POST["addformval"]) : 0;+ global $wpdb;+ global $cnp_table_name;++ if ($addform == 1) {+ $cnprtnval = CNPCF_addSettings($cnp_table_name, $_POST);++ if ($cnprtnval >= 1) {+ $cnpredirectval = "saved";+ } elseif ($cnprtnval == "0") {+ $cnpredirectval = "failed";+ } elseif ($cnprtnval == "error") {+ $cnpredirectval = "exist";+ } else {+ $cnpredirectval = "failed";+ }+ wp_redirect(admin_url("admin.php?page=cnp_formssettings&info=" . $cnpredirectval));+ exit;+ } elseif ($addform == 2) {+ $cnprtnval = CNPCF_updateSettings($cnp_table_name, $_POST);++ if ($cnprtnval >= 1) {+ $cnpredirectval = "upd";+ } else {+ $cnpredirectval = "failed";+ }+ wp_redirect(admin_url("admin.php?page=cnp_formssettings&info=" . $cnpredirectval));+ exit;+ }+}++$act = isset($_REQUEST["cnpviewid"]) ? sanitize_text_field($_REQUEST["cnpviewid"]) : '';++if (!empty($act)) {+ global $wpdb;+ global $cnp_settingtable_name;++ $cnpfrmdtresult = CNPCF_GetCnPGroupDetails($cnp_settingtable_name, 'cnpstngs_ID', intval($act));+ + if (!empty($cnpfrmdtresult)) {+ $cnprtnval = $cnpfrmdtresult[0];++ if (!empty($cnprtnval)) {+ $cnpsetid = $cnprtnval->cnpstngs_ID;+ $cnpsetfrndlynm = $cnprtnval->cnpstngs_frndlyname;+ $cnpsetAccountNumber = $cnprtnval->cnpstngs_AccountNumber;+ $cnpsetguid = $cnprtnval->cnpstngs_guid;+ $cnpbtn = "Update";+ $hidval = 2;+ }+ }+}++?>+<script type="text/javascript">+ /* <![CDATA[ */+ jQuery(document).ready(function(){+ jQuery('#cnpsettingslist').dataTable();+ jQuery("tr:even").css("background-color", "#f1f1f1");+ });+ /* ]]> */++</script>+<?php++if (isset($_REQUEST["act"])) {+ $cnpfrmtyp = wp_unslash(sanitize_text_field($_REQUEST["act"]));+} else {+ $cnpfrmtyp = ''; // Default value+ +}++ if($cnpfrmtyp == "edit"){$msgdsplycntnt = "style ='display:block'";}else{$msgdsplycntnt = "style ='display:none'";}+ if($cnpfrmtyp == "edit"){ $msgdbtnsplycntnt = "style ='display:none'";}else{$msgdbtnsplycntnt = "style ='float:left;display:block'";}++ $cnpresltdsply = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><div class="wrap">+ <h2>Click & Pledge Account </h2>+ <div id="col-left" style="width:48% !important; float:none;">+ <div class="col-wrap">+ <div>+ <div class="form-wrap">+ <table class="form-table" id="cnpformslist" align="center" >++ <tr><td>+ <div> <h2>Add New Account</h2> </div>+ <form class="validate" method="post" id="addsettings" name="addsettings">+ <input type="hidden" name="cnphdnediturl" id="cnphdnediturl" value="'.CNP_CF_PLUGIN_URL.'getcnpditactivecampaigns.php">+ <input type="hidden" name="cnphdnerrurl" id="cnphdnerrurl" value="'.CNP_CF_PLUGIN_URL.'cnpSettingmsgs.php">+ <input type="hidden" name="addformval" id="addformval" value='.$hidval.'>+ <input type="hidden" name="hdnfrmid" id="hdnfrmid" value="'.$cnpsetid .'">++ <div class="form-field cnpaccountId">+ <label for="tag-name"> <b>Click & Pledge Account Number*</b></label>+ <input type="text" size="10" id="txtcnpacntid" name="txtcnpacntid" value="'.$cnpsetAccountNumber.'" />+ <p style="font-size:12px;">Get your "Account Number" from Click & Pledge<br>+[CONNECT > Launcher > Settings > API Information > Account ID]</p>+ <span class=cnperror id="spncnpacntid"></span>+ </div>++ <div class="form-field cnpacntguid">+ <label for="tag-name"> <b>Click & Pledge Account GUID*</b></label>+ <input type="text" size="20" id="txtcnpacntguid" name="txtcnpacntguid" value="'.$cnpsetguid.'" /><div class="tooltip" >+ <i class="fa fa-question-circle"></i>+ <span class="tooltiptext">Please collect the Account GUID from your CONNECT Portal or for More Help <a href="https://support.clickandpledge.com/s/article/how-to-locate-account-id--api-account-guid" target="_blank">click here</a></span>+ </div>+ <p style="font-size:12px;">Get your "Account GUID" from Click & Pledge<br>+ [CONNECT > Launcher > Settings > API Information > API (PaaS / FaaS): Account GUID]</p>+ </div>+ <div '.$msgdbtnsplycntnt.'>+ <input type="button" name="cnpbtnverifysettings" id="cnpbtnverifysettings" value="Verify" class="button button-primary"><br>+ + </div>+ <div class="frmaddnickdiv" '.$msgdsplycntnt.'>+ <div class="form-field cnpfrmfrndlynm" >+ <label for="tag-name">Nickname*</label>+ <input type="text" size="20" id="txtcnpfrmfrndlynm" name="txtcnpfrmfrndlynm" value="'.$cnpsetfrndlynm.'" onkeypress="return AvoidSpace(event)"/>+ <span class=cnperror id="spnfrndlynm"></span>+ </div>++ <div style="float:left">+ <input type="submit" name="cnpbtnaddsettings" id="cnpbtnaddsettings" value="'.$cnpbtn.'" class="button button-primary">+ </div>+</div>+ </form>+ </tr></td></table>+ + </div>+ </div>+ </div>+ </div>+ <div > <div style="float:left" width="100%"><span class="cnperror" id="spnverify" style="display:none"><p>Communication Error:</p>+ +<p>Sorry but I am having difficulty communicating with the Click & Pledge services due to the absence of the SOAP extension in your WordPress installation. The following links may help in resolving this issue:</p>+ +<p>Complete details page: <a href ="http://php.net/manual/en/book.soap.php" target="_blank">http://php.net/manual/en/book.soap.php</a></p>+<p>Installing SOAP for PHP: <a href ="http://php.net/manual/en/soap.installation.php" target="_blank">http://php.net/manual/en/soap.installation.php</a></p>+<p>Configuring after installation: <a href ="http://php.net/manual/en/soap.configuration.php" target="_blank">http://php.net/manual/en/soap.configuration.php</a></p>+ +<p>You may need to contact your server administrator for installation of the SOAP extension on the server.<p></span></div>+ <div class="col-wrap">+ <div>+ <div class="cnp_scrollable_x">+ <table style="width:99%;" class="wp-list-table widefat" id="cnpsettingslist" ><thead><tr><th>Nickname </th><th>Account Number</th><th>GUID</th><th>Created Date/Time</th><th>Actions</th></tr></thead><tbody>';++ $sql = "SELECT * FROM $cnp_settingtable_name ORDER BY cnpstngs_ID DESC";+$result = $wpdb->get_results($sql);++if ($wpdb->num_rows > 0) {+ foreach ($result as $cnpsettingsData) {+ $cnpform_id = intval($cnpsettingsData->cnpstngs_ID);+ $gname = esc_html($cnpsettingsData->cnpstngs_frndlyname);+ $account = esc_html($cnpsettingsData->cnpstngs_AccountNumber);+ $accountguid = esc_html($cnpsettingsData->cnpstngs_guid);+ $frmmodifieddt = new DateTime($cnpsettingsData->cnpstngs_Date_Modified);+ $frmmodifiddt = $frmmodifieddt->format('d-m-Y H:i:s');++ $cnpresltdsply .= '<tr>';+ $cnpresltdsply .= '<td>' . $gname . '</td>';+ $cnpresltdsply .= '<td>' . $account . '</td>';+ $cnpresltdsply .= '<td>' . $accountguid . '</td>';+ $cnpresltdsply .= '<td data-sort="' . $frmmodifieddt->getTimestamp() . '">' . esc_html($frmmodifieddt->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP)) . '</td>';+ $cnpresltdsply .= '<td>';+ $cnpresltdsply .= '<input type="hidden" name="hdnsetngsid' . $cnpform_id . '" id="hdnsetngsid' . $cnpform_id . '" value="' . $cnpform_id . '">';+ $cnpresltdsply .= '<input type="hidden" name="hdncnpaccntid' . $cnpform_id . '" id="hdncnpaccntid' . $cnpform_id . '" value="' . $account . '">';+ $cnpresltdsply .= '<input type="hidden" name="hdncnpguid' . $cnpform_id . '" id="hdncnpguid' . $cnpform_id . '" value="' . $accountguid . '">';+ $cnpresltdsply .= '<input type="hidden" name="hdncnptblname" id="hdncnptblname" value="' . esc_attr($cnp_settingtable_name) . '">';+ $cnpresltdsply .= '<a href="#" id="myHref" onclick="return mycnpaccountId(' . $cnpform_id . ')"><span class="dashicons dashicons-update"></span></a> | ';+ $cnpresltdsply .= '<a href="' . esc_url(admin_url("admin.php?page=cnp_formssettings&info=del&did=$cnpform_id")) . '"><span class="dashicons dashicons-trash"></span></a>';+ $cnpresltdsply .= '</td>';+ $cnpresltdsply .= '</tr>';+ }+} else {+ $cnpresltdsply .= '<tr><td colspan="5">No Record Found!</td></tr>';+}++$cnpresltdsply .= '</tbody></table></div>';+echo $cnpresltdsply;++}+?>
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') - CWE 79 - cnpSettings.php Lines 1-235
Old Code:
```php
$info = $_REQUEST["info"];
...
if($info=="saved")
{
echo "<div class='updated' id='message'><p><strong>Account Added</strong>.</p></div>";
}
...
$delid=$_GET["did"];
...
$act=$_REQUEST["cnpviewid"];
...
$cnpfrmtyp= wp_unslash( sanitize_text_field( $_REQUEST["act"]));
...
$cnpresltdsply .= '<tr><td>'.$gname.'</td><td >'.$account.'</td><td>'.$accountguid.'</td><td data-sort="'.strtotime($frmmodifiddt).'">'.$frmmodifieddt->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP).'</td>
...
<a href="admin.php?page=cnp_formssettings&info=del&did='.$cnpform_id.'" ><span class="dashicons dashicons-trash"></span></a>
```
Fixed Code:
```php
$info = isset($_REQUEST["info"]) ? sanitize_text_field($_REQUEST["info"]) : '';
...
$delid = isset($_GET["did"]) ? intval($_GET["did"]) : 0;
...
$act = isset($_REQUEST["cnpviewid"]) ? sanitize_text_field($_REQUEST["cnpviewid"]) : '';
...
if (isset($_REQUEST["act"])) {
$cnpfrmtyp = wp_unslash(sanitize_text_field($_REQUEST["act"]));
} else {
$cnpfrmtyp = ''; // Default value
}
...
$cnpform_id = intval($cnpsettingsData->cnpstngs_ID);
$gname = esc_html($cnpsettingsData->cnpstngs_frndlyname);
$account = esc_html($cnpsettingsData->cnpstngs_AccountNumber);
$accountguid = esc_html($cnpsettingsData->cnpstngs_guid);
...
$cnpresltdsply .= '<a href="' . esc_url(admin_url("admin.php?page=cnp_formssettings&info=del&did=$cnpform_id")) . '"><span class="dashicons dashicons-trash"></span></a>';
```
Explanation:
The old code directly used `$_REQUEST`, `$_GET`, and unsanitized database values in output, making it vulnerable to XSS attacks. The fixed code introduces proper input validation and output escaping:
- Uses `sanitize_text_field()` on `$_REQUEST["info"]`, `$_REQUEST["cnpviewid"]`, and `$_REQUEST["act"]`
- Uses `intval()` on `$_GET["did"]` for deletion
- Uses `esc_html()` on database values (`$gname`, `$account`, `$accountguid`) before output
- Uses `esc_url()` on generated admin URLs
- Initializes variables to prevent undefined variable warnings
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - CWE 89 - cnpSettings.php Lines 1-235
Old Code:
```php
$delid=$_GET["did"];
...
$wpdb->query("delete from ".$cnp_settingtable_name." where cnpstngs_ID =".$delid);
...
$cnpfrmdtresult = CNPCF_GetCnPGroupDetails($cnp_settingtable_name,'cnpstngs_ID',$_GET['cnpviewid']);
```
Fixed Code:
```php
$delid = isset($_GET["did"]) ? intval($_GET["did"]) : 0;
...
$result = $wpdb->delete(
$cnp_settingtable_name,
['cnpstngs_ID' => $delid],
['%d']
);
...
$cnpfrmdtresult = CNPCF_GetCnPGroupDetails($cnp_settingtable_name, 'cnpstngs_ID', intval($act));
```
Explanation:
The old code concatenated unsanitized user input (`$delid` from `$_GET["did"]`) directly into SQL queries, creating SQL injection vulnerabilities. The fixed code:
- Uses `intval()` to ensure numeric values
- Uses `$wpdb->delete()` with prepared statement placeholders (`['%d']`) for safe deletion
- Passes sanitized integer to `CNPCF_GetCnPGroupDetails()` function
Vulnerability Existed: yes
TRUE POSITIVE
CWE-20: Improper Input Validation - CWE 20 - cnpSettings.php Lines 1-235
Old Code:
```php
$info = $_REQUEST["info"];
$delid=$_GET["did"];
$act=$_REQUEST["cnpviewid"];
$addform=$_POST["addformval"];
```
Fixed Code:
```php
$info = isset($_REQUEST["info"]) ? sanitize_text_field($_REQUEST["info"]) : '';
$delid = isset($_GET["did"]) ? intval($_GET["did"]) : 0;
$act = isset($_REQUEST["cnpviewid"]) ? sanitize_text_field($_REQUEST["cnpviewid"]) : '';
$addform = isset($_POST["addformval"]) ? intval($_POST["addformval"]) : 0;
```
Explanation:
The old code failed to validate the existence of input variables before use, which could lead to undefined variable warnings and unpredictable behavior. The fixed code checks variable existence with `isset()` and provides default values, preventing PHP notices and improving security through proper input validation.
CVE Analysis Results:
CVE-2025-32550: Yes
View CVE Description
Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in ClickandPledge Click & Pledge Connect Plugin allows SQL Injection. This issue affects Click & Pledge Connect Plugin: from 2.24080000 through WP6.6.1.
[CVE-2025-32550] [Click & Pledge CONNECT: 2.24080000-WP6.6.1→2.24120000-WP6.7.1] cnptvchannelsDetails.php
AI: 3 vulnerabilities
3 true positives
CVE-2025-32550
--- cache/click-pledge-connect_2.24080000-WP6.6.1/cnptvchannelsDetails.php 2025-12-04 14:06:23.600345305 +0000+++ cache/click-pledge-connect_2.24120000-WP6.7.1/cnptvchannelsDetails.php 2025-12-04 14:06:27.808615423 +0000@@ -1,91 +1,142 @@-<?php -function cnp_pledgetvchannelsdetails() { - - global $wpdb; global $cnp_channelgrptable_name;global $cnp_channeltable_name;global $cnp_settingtable_name; - $info = $_REQUEST["info"]; - $cnpresltdsply = ""; - if($info=="saved") - { - echo "<div class='updated' id='message'><p><strong>Channel Added</strong>.</p></div>"; - } - if($info=="failed") - { - echo "<div class='updated' id='message'><p><strong>Already Existed</strong>.</p></div>"; - } - if($info=="upd") - { - echo "<div class='updated' id='message'><p><strong>Channel updated</strong>.</p></div>"; - } - if($info=="sts") - { - echo "<div class='updated' id='message'><p><strong>Status updated</strong>.</p></div>"; - } - if($info=="del") - { - $delid=$_GET["did"]; - $wpdb->query("delete from ".$cnp_channelgrptable_name." where cnpchannelgrp_ID =".$delid); - echo "<div class='updated' id='message'><p><strong>Record Deleted.</strong>.</p></div>"; - } - if(isset($_GET['cnpsts']) && $_GET['cnpsts'] !="") - { - $cnpstsrtnval = CNPCF_updateCnPstatus($cnp_channelgrptable_name,'cnpchannelgrp_status','cnpchannelgrp_ID',$_GET['cnpviewid'],$_GET['cnpsts']); - if($cnpstsrtnval == true){$cnpredirectval = "sts";}else{$cnpredirectval = "stsfail";} - wp_redirect("admin.php?page=cnp_pledgetvchannelsdetails&info=".$cnpredirectval); - exit; - } - -?> -<script type="text/javascript"> - /* <![CDATA[ */ - jQuery(document).ready(function(){ - jQuery('#cnpformslist').dataTable(); - jQuery("tr:even").css("background-color", "#f1f1f1"); - }); - /* ]]> */ - -</script> -<?php - $cnpresltdsply = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><div class="wrap"> - <h2>pledgeTV<sup class="cnpc-regsymbol">®</sup> Channels <a class="page-title-action add-new-h2" href="admin.php?page=cnps_addchannel&act=add">Add New Channel Group</a></h2><p></p> - <table class="wp-list-table widefat cnp_table_w" id="cnpformslist" ><thead><tr><th>Group Name</th><th>Account #</th><th>Short Code <a class="tooltip" ><i class="fa fa-question-circle"></i><span class="tooltiptext">Please copy this code and place it in your required content pages, posts or any custom content types. This code will run the series of the channels which has been added to this particular channel Group inside your content page.</span></a></th><th>Start Date/Time</th><th>End Date/Time</th><th>Active Channel(s)</th><th>Last Modified</th><th>Status</th><th>Actions</th></tr></thead><tbody>'; - - $sql = "select * from ".$cnp_channelgrptable_name." join ".$cnp_settingtable_name." on cnpchannelgrp_cnpstngs_ID= cnpstngs_ID order by cnpchannelgrp_ID desc"; - $result = $wpdb->get_results($sql); - if($wpdb->num_rows > 0 ) - { - foreach($result as $cnpformData): - //<td nowrap><u><a href="admin.php?page=cnpform_add&cnpid='.$id.'"">Edit</a></u></td> - $nwenddt=""; - $cnpform_id = $cnpformData->cnpchannelgrp_ID; - $gname = $cnpformData->cnpchannelgrp_groupname; - $account = $cnpformData->cnpstngs_AccountNumber; - $frmstrtdt = $cnpformData->cnpchannelgrp_channel_StartDate; - $frmenddt = $cnpformData->cnpchannelgrp_channel_EndDate; - if($frmenddt == "0000-00-00 00:00:00") {$frmenddt ="";} - - $frmshrtcode = $cnpformData->cnpchannelgrp_shortcode; - $stdate = new DateTime($frmstrtdt); - if($frmenddt!=""){ - $eddate = new DateTime($frmenddt); - $nwenddt = $eddate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP);} - $mddate = new DateTime($cnpformData->cnpchannelgrp_Date_Modified); - $frmmodifiddt = date_format(date_create($cnpformData->cnpchannelgrp_Date_Modified),"d-m-Y H:i:s"); - $frmstrtddt = date_format(date_create($cnpformData->cnpchannelgrp_channel_StartDate),"d-m-Y H:i:s"); - $frmsts = CNPCF_getfrmsts($cnp_channelgrptable_name,'cnpchannelgrp_status','cnpchannelgrp_ID',$cnpform_id); - if($frmenddt!=""){ - if(strtotime($frmenddt) < strtotime(CFCNP_PLUGIN_CURRENTTIME)){ - $frmsts ="Expired"; - } - } - $noofchannels = CNPCF_getCountChannels($cnpform_id); - $cnpresltdsply .= '<tr><td>'.$gname.'</td><td>'.$account.'</td><td>'.$frmshrtcode.'</td><td data-sort="'.strtotime($frmstrtddt).'">'.$stdate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP).'</td><td>'.$nwenddt.'</td><td align="center">'.$noofchannels.'</td><td data-sort="'.strtotime($frmmodifiddt).'">'.$mddate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP).'</td> - <td><a href="admin.php?page=cnp_pledgetvchannelsdetails&cnpsts='.$frmsts.'&cnpviewid='.$cnpform_id.'"">'.$frmsts.'</a></td> - <td><a href="admin.php?page=cnp_channeldetails&cnpviewid='.$cnpform_id.'""><span class="dashicons dashicons-visibility"></span></a> | <a href="admin.php?page=cnps_addchannel&act=edit&cnpviewid='.$cnpform_id.'""><span class="dashicons dashicons-edit"></span></a> | <a href="admin.php?page=cnp_pledgetvchannelsdetails&info=del&did='.$cnpform_id.'" ><span class="dashicons dashicons-trash"></span></a></td></tr>'; - endforeach; - } - else {$cnpresltdsply .= '<tr><td>No Record Found!</td><tr>';} - - $cnpresltdsply .= '</tbody></table></div>'; - echo $cnpresltdsply ; -} +<?php+function cnp_pledgetvchannelsdetails() {++ global $wpdb; global $cnp_channelgrptable_name;global $cnp_channeltable_name;global $cnp_settingtable_name;+ if (isset($_REQUEST["info"])) { $info = sanitize_text_field($_REQUEST["info"]); }+ $cnpresltdsply = "";+ if(isset($info) && $info ==="saved")+ {+ echo "<div class='updated' id='message'><p><strong>Channel Added</strong>.</p></div>";+ }+ if(isset($info) && $info ==="failed")+ {+ echo "<div class='updated' id='message'><p><strong>Already Existed</strong>.</p></div>";+ }+ if(isset($info) && $info ==="upd")+ {+ echo "<div class='updated' id='message'><p><strong>Channel updated</strong>.</p></div>";+ }+ if(isset($info) && $info ==="sts")+ {+ echo "<div class='updated' id='message'><p><strong>Status updated</strong>.</p></div>";+ }+ if (isset($info) && $info === "del") {+ + $delid = isset($_GET['did']) ? intval($_GET['did']) : 0;++ if ($delid > 0) {+ $deleted = $wpdb->query($wpdb->prepare("DELETE FROM $cnp_channelgrptable_name WHERE cnpchannelgrp_ID = %d", $delid));+ if ($deleted) {+ echo "<div class='updated' id='message'><p><strong>Record Deleted.</strong></p></div>";+ } else {+ echo "<div class='error' id='message'><p><strong>Error deleting record.</strong></p></div>";+ }+ } else {+ echo "<div class='error' id='message'><p><strong>Invalid ID.</strong></p></div>";+ }+}++if (isset($_GET['cnpsts']) && !empty($_GET['cnpsts'])) {++ $cnpsts = sanitize_text_field($_GET['cnpsts']);+ $cnpviewid = isset($_GET['cnpviewid']) ? intval($_GET['cnpviewid']) : 0;++ if ($cnpviewid > 0 && in_array($cnpsts, ['active', 'inactive'], true)) { + + $cnpstsrtnval = CNPCF_updateCnPstatus($cnp_channelgrptable_name, 'cnpchannelgrp_status', 'cnpchannelgrp_ID', $cnpviewid, $cnpsts);++ if ($cnpstsrtnval) {+ $cnpredirectval = "sts";+ } else {+ $cnpredirectval = "stsfail";+ }++ wp_redirect("admin.php?page=cnp_pledgetvchannelsdetails&info=" . $cnpredirectval);+ exit;+ } else {+ echo "<div class='error' id='message'><p><strong>Invalid status or ID.</strong></p></div>";+ }+}+++?>+<script type="text/javascript">+ /* <![CDATA[ */+ jQuery(document).ready(function(){+ jQuery('#cnpformslist').dataTable();+ jQuery("tr:even").css("background-color", "#f1f1f1");+ });+ /* ]]> */++</script>+<?php+ $cnpresltdsply = '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"><div class="wrap">+ <h2>pledgeTV<sup class="cnpc-regsymbol">®</sup> Channels <a class="page-title-action add-new-h2" href="admin.php?page=cnps_addchannel&act=add">Add New Channel Group</a></h2><p></p>+ <table class="wp-list-table widefat cnp_table_w" id="cnpformslist" ><thead><tr><th>Group Name</th><th>Account #</th><th>Short Code <a class="tooltip" ><i class="fa fa-question-circle"></i><span class="tooltiptext">Please copy this code and place it in your required content pages, posts or any custom content types. This code will run the series of the channels which has been added to this particular channel Group inside your content page.</span></a></th><th>Start Date/Time</th><th>End Date/Time</th><th>Active Channel(s)</th><th>Last Modified</th><th>Status</th><th>Actions</th></tr></thead><tbody>';++ $sql = $wpdb->prepare("SELECT * + FROM $cnp_channelgrptable_name + JOIN $cnp_settingtable_name + ON cnpchannelgrp_cnpstngs_ID = cnpstngs_ID + ORDER BY cnpchannelgrp_ID DESC");+$result = $wpdb->get_results($sql);++if ($wpdb->num_rows > 0) {+ foreach ($result as $cnpformData) {+ $nwenddt = "";+ $cnpform_id = $cnpformData->cnpchannelgrp_ID;+ $gname = esc_html($cnpformData->cnpchannelgrp_groupname);+ $account = esc_html($cnpformData->cnpstngs_AccountNumber);+ $frmstrtdt = $cnpformData->cnpchannelgrp_channel_StartDate;+ $frmenddt = $cnpformData->cnpchannelgrp_channel_EndDate;++ if ($frmenddt == "0000-00-00 00:00:00") {+ $frmenddt = "";+ }++ $frmshrtcode = esc_html($cnpformData->cnpchannelgrp_shortcode);+ $stdate = new DateTime($frmstrtdt);++ if ($frmenddt != "") {+ $eddate = new DateTime($frmenddt);+ $nwenddt = $eddate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP);+ }++ $mddate = new DateTime($cnpformData->cnpchannelgrp_Date_Modified);+ $frmmodifiddt = date_format(date_create($cnpformData->cnpchannelgrp_Date_Modified), "d-m-Y H:i:s");+ $frmstrtddt = date_format(date_create($cnpformData->cnpchannelgrp_channel_StartDate), "d-m-Y H:i:s");++ $frmsts = CNPCF_getfrmsts($cnp_channelgrptable_name, 'cnpchannelgrp_status', 'cnpchannelgrp_ID', $cnpform_id);++ if ($frmenddt != "" && strtotime($frmenddt) < strtotime(CFCNP_PLUGIN_CURRENTTIME)) {+ $frmsts = "Expired";+ }++ $noofchannels = CNPCF_getCountChannels($cnpform_id);++ // Build the result display+ $cnpresltdsply .= '<tr>+ <td>' . esc_html($gname) . '</td>+ <td>' . esc_html($account) . '</td>+ <td>' . esc_html($frmshrtcode) . '</td>+ <td data-sort="' . strtotime($frmstrtddt) . '">' . $stdate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP) . '</td>+ <td>' . $nwenddt . '</td>+ <td align="center">' . esc_html($noofchannels) . '</td>+ <td data-sort="' . strtotime($frmmodifiddt) . '">' . $mddate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP) . '</td>+ <td><a href="admin.php?page=cnp_pledgetvchannelsdetails&cnpsts=' . esc_attr($frmsts) . '&cnpviewid=' . esc_attr($cnpform_id) . '">' . esc_html($frmsts) . '</a></td>+ <td>+ <a href="admin.php?page=cnp_channeldetails&cnpviewid=' . esc_attr($cnpform_id) . '"><span class="dashicons dashicons-visibility"></span></a> |+ <a href="admin.php?page=cnps_addchannel&act=edit&cnpviewid=' . esc_attr($cnpform_id) . '"><span class="dashicons dashicons-edit"></span></a> |+ <a href="admin.php?page=cnp_pledgetvchannelsdetails&info=del&did=' . esc_attr($cnpform_id) . '"><span class="dashicons dashicons-trash"></span></a>+ </td>+ </tr>';+ }+} else {+ $cnpresltdsply .= '<tr><td colspan="7">No Record Found!</td></tr>';+}++ + $cnpresltdsply .= '</tbody></table></div>';+ echo $cnpresltdsply ;+} ?>\ No newline at end of file
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - cnptvchannelsDetails.php lines 24, 37, 71
Old Code:
```php
$delid=$_GET["did"];
$wpdb->query("delete from ".$cnp_channelgrptable_name." where cnpchannelgrp_ID =".$delid);
```
```php
if(isset($_GET['cnpsts']) && $_GET['cnpsts'] !="")
{
$cnpstsrtnval = CNPCF_updateCnPstatus($cnp_channelgrptable_name,'cnpchannelgrp_status','cnpchannelgrp_ID',$_GET['cnpviewid'],$_GET['cnpsts']);
```
```php
$sql = "select * from ".$cnp_channelgrptable_name." join ".$cnp_settingtable_name." on cnpchannelgrp_cnpstngs_ID= cnpstngs_ID order by cnpchannelgrp_ID desc";
```
Fixed Code:
```php
$delid = isset($_GET['did']) ? intval($_GET['did']) : 0;
if ($delid > 0) {
$deleted = $wpdb->query($wpdb->prepare("DELETE FROM $cnp_channelgrptable_name WHERE cnpchannelgrp_ID = %d", $delid));
```
```php
if (isset($_GET['cnpsts']) && !empty($_GET['cnpsts'])) {
$cnpsts = sanitize_text_field($_GET['cnpsts']);
$cnpviewid = isset($_GET['cnpviewid']) ? intval($_GET['cnpviewid']) : 0;
if ($cnpviewid > 0 && in_array($cnpsts, ['active', 'inactive'], true)) {
$cnpstsrtnval = CNPCF_updateCnPstatus($cnp_channelgrptable_name, 'cnpchannelgrp_status', 'cnpchannelgrp_ID', $cnpviewid, $cnpsts);
```
```php
$sql = $wpdb->prepare("SELECT *
FROM $cnp_channelgrptable_name
JOIN $cnp_settingtable_name
ON cnpchannelgrp_cnpstngs_ID = cnpstngs_ID
ORDER BY cnpchannelgrp_ID DESC");
```
Explanation:
The old code directly concatenated user input (`$_GET['did']`, `$_GET['cnpviewid']`, `$_GET['cnpsts']`) into SQL queries without validation or sanitization, making it vulnerable to SQL injection. The fixed code uses `intval()` for integer parameters, `sanitize_text_field()` for text, and `$wpdb->prepare()` with placeholders to safely construct queries, preventing malicious SQL execution.
Vulnerability Existed: yes
TRUE POSITIVE
CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') - cnptvchannelsDetails.php lines 84-85, 103-108
Old Code:
```php
$gname = $cnpformData->cnpchannelgrp_groupname;
$account = $cnpformData->cnpstngs_AccountNumber;
```
```php
$cnpresltdsply .= '<tr><td>'.$gname.'</td><td>'.$account.'</td><td>'.$frmshrtcode.'</td><td data-sort="'.strtotime($frmstrtddt).'">'.$stdate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP).'</td><td>'.$nwenddt.'</td><td align="center">'.$noofchannels.'</td><td data-sort="'.strtotime($frmmodifiddt).'">'.$mddate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP).'</td>
<td><a href="admin.php?page=cnp_pledgetvchannelsdetails&cnpsts='.$frmsts.'&cnpviewid='.$cnpform_id.'"">'.$frmsts.'</a></td>
<td><a href="admin.php?page=cnp_channeldetails&cnpviewid='.$cnpform_id.'""><span class="dashicons dashicons-visibility"></span></a> | <a href="admin.php?page=cnps_addchannel&act=edit&cnpviewid='.$cnpform_id.'""><span class="dashicons dashicons-edit"></span></a> | <a href="admin.php?page=cnp_pledgetvchannelsdetails&info=del&did='.$cnpform_id.'" ><span class="dashicons dashicons-trash"></span></a></td></tr>';
```
Fixed Code:
```php
$gname = esc_html($cnpformData->cnpchannelgrp_groupname);
$account = esc_html($cnpformData->cnpstngs_AccountNumber);
```
```php
$cnpresltdsply .= '<tr>
<td>' . esc_html($gname) . '</td>
<td>' . esc_html($account) . '</td>
<td>' . esc_html($frmshrtcode) . '</td>
<td data-sort="' . strtotime($frmstrtddt) . '">' . $stdate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP) . '</td>
<td>' . $nwenddt . '</td>
<td align="center">' . esc_html($noofchannels) . '</td>
<td data-sort="' . strtotime($frmmodifiddt) . '">' . $mddate->format(CFCNP_PLUGIN_CURRENTDATETIMEFORMATPHP) . '</td>
<td><a href="admin.php?page=cnp_pledgetvchannelsdetails&cnpsts=' . esc_attr($frmsts) . '&cnpviewid=' . esc_attr($cnpform_id) . '">' . esc_html($frmsts) . '</a></td>
<td>
<a href="admin.php?page=cnp_channeldetails&cnpviewid=' . esc_attr($cnpform_id) . '"><span class="dashicons dashicons-visibility"></span></a> |
<a href="admin.php?page=cnps_addchannel&act=edit&cnpviewid=' . esc_attr($cnpform_id) . '"><span class="dashicons dashicons-edit"></span></a> |
<a href="admin.php?page=cnp_pledgetvchannelsdetails&info=del&did=' . esc_attr($cnpform_id) . '"><span class="dashicons dashicons-trash"></span></a>
</td>
</tr>';
```
Explanation:
The old code directly output database values (`$gname`, `$account`, `$frmshrtcode`, `$frmsts`, `$cnpform_id`) into HTML without escaping, allowing stored XSS if malicious data is inserted into the database. The fixed code uses `esc_html()` for text content and `esc_attr()` for HTML attributes, neutralizing dangerous characters and preventing script injection.
Vulnerability Existed: yes
TRUE POSITIVE
CWE-20: Improper Input Validation - cnptvchannelsDetails.php lines 2, 24, 37
Old Code:
```php
$info = $_REQUEST["info"];
```
```php
$delid=$_GET["did"];
$wpdb->query("delete from ".$cnp_channelgrptable_name." where cnpchannelgrp_ID =".$delid);
```
```php
if(isset($_GET['cnpsts']) && $_GET['cnpsts'] !="")
{
$cnpstsrtnval = CNPCF_updateCnPstatus($cnp_channelgrptable_name,'cnpchannelgrp_status','cnpchannelgrp_ID',$_GET['cnpviewid'],$_GET['cnpsts']);
```
Fixed Code:
```php
if (isset($_REQUEST["info"])) { $info = sanitize_text_field($_REQUEST["info"]); }
```
```php
$delid = isset($_GET['did']) ? intval($_GET['did']) : 0;
if ($delid > 0) {
$deleted = $wpdb->query($wpdb->prepare("DELETE FROM $cnp_channelgrptable_name WHERE cnpchannelgrp_ID = %d", $delid));
```
```php
if (isset($_GET['cnpsts']) && !empty($_GET['cnpsts'])) {
$cnpsts = sanitize_text_field($_GET['cnpsts']);
$cnpviewid = isset($_GET['cnpviewid']) ? intval($_GET['cnpviewid']) : 0;
if ($cnpviewid > 0 && in_array($cnpsts, ['active', 'inactive'], true)) {
$cnpstsrtnval = CNPCF_updateCnPstatus($cnp_channelgrptable_name, 'cnpchannelgrp_status', 'cnpchannelgrp_ID', $cnpviewid, $cnpsts);
```
Explanation:
The old code lacked input validation: `$_REQUEST["info"]` was used unsanitized, `$_GET['did']` and `$_GET['cnpviewid']` were not checked for existence or type, and `$_GET['cnpsts']` had no validation for allowed values. The fixed code adds checks with `isset()`, uses `intval()` for IDs, `sanitize_text_field()` for text, and `in_array()` to restrict status values, preventing invalid or malicious input from causing errors or security issues.
CVE Analysis Results:
CVE-2025-32550: Yes
View CVE Description
Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in ClickandPledge Click & Pledge Connect Plugin allows SQL Injection. This issue affects Click & Pledge Connect Plugin: from 2.24080000 through WP6.6.1.
[CVE-2025-54726] [JS Archive List: 6.1.5→6.1.6] classes/class-jq-archive-list-datasource.php
AI: 4 vulnerabilities
4 true positives
CVE-2025-54726
--- cache/jquery-archive-list-widget_6.1.5/classes/class-jq-archive-list-datasource.php 2025-12-04 14:06:22.160252868 +0000+++ cache/jquery-archive-list-widget_6.1.6/classes/class-jq-archive-list-datasource.php 2025-12-04 14:05:29.716886575 +0000@@ -1,142 +1,186 @@ <?php+declare(strict_types=1); defined( 'ABSPATH' ) or die( 'No script kiddies please!' ); /** * Class to load required information from DB.+ *+ * @property array $config Configuration options for data source+ * @property bool $legacy Legacy mode flag */ class JQ_Archive_List_DataSource {- public $config;- public $legacy;-- public function __construct( $config = [], $legacy = false ) {- $this->config = $config;- $this->legacy = $legacy;- }-- public function get_years() {- global $wpdb;- $sql = sprintf(- 'SELECT JAL.year, COUNT(JAL.ID) as `posts` FROM (- SELECT DISTINCT YEAR(post_date) AS `year`, ID- FROM %s %s %s) JAL- GROUP BY JAL.year ORDER BY JAL.year DESC',- $wpdb->posts,- $this->build_sql_join(),- $this->build_sql_where()- );-- return $wpdb->get_results( $sql );- }-- protected function build_sql_join() {- global $wpdb;-- $join = '';-- if ( $this->has_filtering_categories() || $this->only_show_cur_category() ) {- $join = sprintf( ' LEFT JOIN %s ON(%s.ID = %s.object_id)',- $wpdb->term_relationships, $wpdb->posts, $wpdb->term_relationships- );-- $join .= sprintf( ' LEFT JOIN %s ON(%s.term_taxonomy_id = %s.term_taxonomy_id)',- $wpdb->term_taxonomy, $wpdb->term_relationships, $wpdb->term_taxonomy- );- }-- return apply_filters( 'getarchives_join', $join, [] );- }-- /**- * Check if user selected categories for inclusion or exclusion.- *- * @return bool If there are saved categories for including/excluding.- */- private function has_filtering_categories() {- return ! empty( $this->config['included'] ) || ! empty( $this->config['excluded'] );- }-- /**- * Returns if the option to show only current categories- * was selected and current page is a category page.- *- * @return bool Show only current category.- */- private function only_show_cur_category() {- return ! empty( $this->config['onlycategory'] );- }-- protected function build_sql_where( $year = null, $month = null ) {- global $wpdb;-- $where = sprintf( 'WHERE post_title != \'\' AND post_type = \'%s\' AND post_status = \'publish\' ', $this->config['type'] );-- if ( $year ) {- $where .= sprintf( 'AND YEAR(post_date) = %s ', $year );- }-- if ( $month ) {- $where .= sprintf( 'AND MONTH(post_date) = %s ', $month );- }-- if ( ! empty( $this->config['included'] ) ) {- $ids = is_array( $this->config['included'] ) ? implode( ',', $this->config['included'] ) : $this->config['included'];- $where .= sprintf( 'AND %s.term_id IN (%s)', $wpdb->term_taxonomy, $ids );- } elseif ( ! empty( $this->config['excluded'] ) ) {- $ids = is_array( $this->config['excluded'] ) ? implode( ',', $this->config['excluded'] ) : $this->config['excluded'];- $where .= sprintf( 'AND %s.term_id NOT IN (%s)', $wpdb->term_taxonomy, $ids );- }-- if ( $this->only_show_cur_category() ) {- // Leave config when removing legacy code.- $query_cat = get_query_var( 'cat' );- $categories_ids = empty( $query_cat ) ? $this->config['onlycategory'] : $query_cat;-- if ( ( $this->legacy && is_category() || ! $this->legacy ) ) {- $where .= sprintf( 'AND %s.term_id IN (%s) ', $wpdb->term_taxonomy, $categories_ids );- }- }-- if ( $this->has_filtering_categories() || $this->only_show_cur_category() ) {- $where .= 'AND ' . $wpdb->term_taxonomy . '.taxonomy=\'category\' ';- }-- return apply_filters( 'getarchives_where', $where, [] );- }-- public function get_posts( $year, $month ) {- global $wpdb;-- if ( empty( $year ) || empty( $month ) ) {- return null;- }-- $sort = isset( $this->config['sort'] ) ? $this->config['sort'] : 'date_desc';- $order_by = explode( '_', $sort );-- return $wpdb->get_results( sprintf(- 'SELECT DISTINCT ID, post_title, post_name, post_date, "false" as expanded FROM %s %s %s ORDER BY %s %s',- $wpdb->posts, $this->build_sql_join(), $this->build_sql_where( $year, $month ),- $this->query_val_to_field_name( $order_by[0] ),- $order_by[1]- ) );- }-- /**- * Maps the query value to the correct DB field.- *- * @param string $query_value received field name from the frontend.- *- * @return string DB field name.- */- private function query_val_to_field_name( $query_value ): string {- $map = [- 'id' => 'ID',- 'name' => 'post_title',- 'date' => 'post_date',- ];-- return $map[ $query_value ];- }+ /**+ * @var array Configuration options+ */+ private $config;+ /**+ * @var bool Legacy mode flag+ */+ private $legacy;++ /**+ * Constructor+ *+ * @param array $config+ * @param bool $legacy+ */+ public function __construct(array $config = [], bool $legacy = false) {+ $this->config = $config;+ $this->legacy = $legacy;+ }++ /**+ * Get years and post counts+ *+ * @return array|null+ */+ public function get_years(): ?array {+ global $wpdb;++ list($where_clause, $where_args) = $this->build_sql_where();++ $sql = "SELECT JAL.year, COUNT(JAL.ID) as `posts` FROM (+ SELECT DISTINCT YEAR(post_date) AS `year`, ID+ FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause}) JAL+ GROUP BY JAL.year ORDER BY JAL.year DESC";++ $results = $wpdb->get_results($wpdb->prepare($sql, ...$where_args));+ return is_array($results) ? $results : null;+ }++ /**+ * Build SQL JOIN clause+ *+ * @return string+ */+ protected function build_sql_join(): string {+ global $wpdb;+ $join = '';+ if ($this->has_filtering_categories() || $this->only_show_cur_category()) {+ $join = sprintf(' LEFT JOIN %s ON(%s.ID = %s.object_id)',+ $wpdb->term_relationships, $wpdb->posts, $wpdb->term_relationships+ );+ $join .= sprintf(' LEFT JOIN %s ON(%s.term_taxonomy_id = %s.term_taxonomy_id)',+ $wpdb->term_taxonomy, $wpdb->term_relationships, $wpdb->term_taxonomy+ );+ }+ return apply_filters('getarchives_join', $join, []);+ }++ /**+ * Check if user selected categories for inclusion or exclusion.+ *+ * @return bool+ */+ private function has_filtering_categories(): bool {+ return !empty($this->config['included']) || !empty($this->config['excluded']);+ }++ /**+ * Returns if the option to show only current categories was selected and current page is a category page.+ *+ * @return bool+ */+ private function only_show_cur_category(): bool {+ return !empty($this->config['onlycategory']);+ }++ /**+ * Build SQL WHERE clause+ *+ * @param int|null $year+ * @param int|null $month+ * @return array+ */+ protected function build_sql_where(?int $year = null, ?int $month = null): array {+ global $wpdb;+ $where_parts = [];+ $prepare_args = [];+ $where_parts[] = 'post_title != %s';+ $prepare_args[] = '';+ $where_parts[] = 'post_type = %s';+ $prepare_args[] = $this->config['type'] ?? 'post';+ $where_parts[] = 'post_status = %s';+ $prepare_args[] = 'publish';+ if ($year) {+ $where_parts[] = 'YEAR(post_date) = %d';+ $prepare_args[] = $year;+ }+ if ($month) {+ $where_parts[] = 'MONTH(post_date) = %d';+ $prepare_args[] = $month;+ }+ $ids_key = !empty($this->config['included']) ? 'included' : (!empty($this->config['excluded']) ? 'excluded' : null);+ if ($ids_key) {+ $ids = is_array($this->config[$ids_key]) ? $this->config[$ids_key] : explode(',', $this->config[$ids_key]);+ $ids = array_map('intval', $ids);+ $placeholders = implode(', ', array_fill(0, count($ids), '%d'));+ $operator = $ids_key === 'included' ? 'IN' : 'NOT IN';+ $where_parts[] = sprintf('%s.term_id %s (%s)', $wpdb->term_taxonomy, $operator, $placeholders);+ $prepare_args = array_merge($prepare_args, $ids);+ }+ if ($this->only_show_cur_category()) {+ $query_cat = get_query_var('cat');+ $categories_ids = empty($query_cat) ? $this->config['onlycategory'] : $query_cat;+ $categories_ids = is_array($categories_ids) ? $categories_ids : explode(',', $categories_ids);+ $categories_ids = array_map('intval', $categories_ids);+ if (($this->legacy && is_category()) || !$this->legacy) {+ $placeholders = implode(', ', array_fill(0, count($categories_ids), '%d'));+ $where_parts[] = sprintf('%s.term_id IN (%s)', $wpdb->term_taxonomy, $placeholders);+ $prepare_args = array_merge($prepare_args, $categories_ids);+ }+ }+ if ($this->has_filtering_categories() || $this->only_show_cur_category()) {+ $where_parts[] = $wpdb->term_taxonomy . '.taxonomy=%s';+ $prepare_args[] = 'category';+ }+ $where_clause_fragment = implode(' AND ', $where_parts);+ $where_clause_fragment = apply_filters('getarchives_where', $where_clause_fragment, []);+ return [$where_clause_fragment, $prepare_args];+ }++ /**+ * Get posts for a given year and month+ *+ * @param int $year+ * @param int $month+ * @return array|null+ */+ public function get_posts(int $year, int $month): ?array {+ global $wpdb;+ if ($year <= 0 || $month <= 0 || $month > 12) {+ return null;+ }+ $sort = $this->config['sort'] ?? 'date_desc';+ $order_by = explode('_', $sort);+ $order_direction = strtoupper($order_by[1] ?? 'DESC');+ if (!in_array($order_direction, ['ASC', 'DESC'], true)) {+ $order_direction = 'DESC';+ }+ list($where_clause, $where_args) = $this->build_sql_where( $year, $month);+ $order_field = $this->query_val_to_field_name($order_by[0] ?? 'date');+ $sql = "SELECT DISTINCT ID, post_title, post_name, post_date, 'false' as expanded FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause} ORDER BY {$order_field} {$order_direction}";+ $results = $wpdb->get_results($wpdb->prepare($sql, ...$where_args));+ return is_array($results) ? $results : null;+ }++ /**+ * Maps the query value to the correct DB field.+ *+ * @param string $query_value+ * @return string+ */+ protected function query_val_to_field_name(string $query_value): string {+ switch ($query_value) {+ case 'title':+ return 'post_title';+ case 'name':+ return 'post_name';+ case 'date':+ default:+ return 'post_date';+ }+ } public function year_should_be_expanded( $year, $cur_post_year, $cur_post_month, $expandConfig ): bool { $months = $this->get_months( $year );@@ -160,13 +204,13 @@ public function get_months( $year ) { global $wpdb;+ list($where_clause, $where_args) = $this->build_sql_where(intval($year));+ $sql = "SELECT JAL.year, JAL.month, COUNT(JAL.ID) as `posts` FROM (+ SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, ID+ FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause}) JAL+ GROUP BY JAL.year, JAL.month ORDER BY JAL.year, JAL.month DESC"; - return $wpdb->get_results( sprintf(- 'SELECT JAL.year, JAL.month, COUNT(JAL.ID) as `posts` FROM (- SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`,ID FROM %s %s %s)- JAL GROUP BY JAL.year, JAL.month ORDER BY JAL.year,JAL.month DESC',- $wpdb->posts, $this->build_sql_join(), $this->build_sql_where( $year )- ) );+ return $wpdb->get_results($wpdb->prepare($sql, ...$where_args)); } }
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - classes/class-jq-archive-list-datasource.php [Lines 39-44, 94-136, 159-161, 204-210]
Old Code:
```php
$sql = sprintf(
'SELECT JAL.year, COUNT(JAL.ID) as `posts` FROM (
SELECT DISTINCT YEAR(post_date) AS `year`, ID
FROM %s %s %s) JAL
GROUP BY JAL.year ORDER BY JAL.year DESC',
$wpdb->posts,
$this->build_sql_join(),
$this->build_sql_where()
);
return $wpdb->get_results( $sql );
```
Fixed Code:
```php
list($where_clause, $where_args) = $this->build_sql_where();
$sql = "SELECT JAL.year, COUNT(JAL.ID) as `posts` FROM (
SELECT DISTINCT YEAR(post_date) AS `year`, ID
FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause}) JAL
GROUP BY JAL.year ORDER BY JAL.year DESC";
$results = $wpdb->get_results($wpdb->prepare($sql, ...$where_args));
return is_array($results) ? $results : null;
```
Explanation:
The old code directly concatenated user-controlled values (like `$this->config['included']`, `$this->config['excluded']`, `$this->config['type']`, etc.) into SQL strings using `sprintf()`, which could lead to SQL injection if those values contain malicious SQL fragments. The new code uses `$wpdb->prepare()` with parameterized queries, ensuring proper escaping and neutralization of special elements.
---
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - classes/class-jq-archive-list-datasource.php [Lines 94-136]
Old Code:
```php
$where = sprintf( 'WHERE post_title != \'\' AND post_type = \'%s\' AND post_status = \'publish\' ', $this->config['type'] );
if ( $year ) {
$where .= sprintf( 'AND YEAR(post_date) = %s ', $year );
}
if ( $month ) {
$where .= sprintf( 'AND MONTH(post_date) = %s ', $month );
}
if ( ! empty( $this->config['included'] ) ) {
$ids = is_array( $this->config['included'] ) ? implode( ',', $this->config['included'] ) : $this->config['included'];
$where .= sprintf( 'AND %s.term_id IN (%s)', $wpdb->term_taxonomy, $ids );
} elseif ( ! empty( $this->config['excluded'] ) ) {
$ids = is_array( $this->config['excluded'] ) ? implode( ',', $this->config['excluded'] ) : $this->config['excluded'];
$where .= sprintf( 'AND %s.term_id NOT IN (%s)', $wpdb->term_taxonomy, $ids );
}
```
Fixed Code:
```php
$where_parts = [];
$prepare_args = [];
$where_parts[] = 'post_title != %s';
$prepare_args[] = '';
$where_parts[] = 'post_type = %s';
$prepare_args[] = $this->config['type'] ?? 'post';
$where_parts[] = 'post_status = %s';
$prepare_args[] = 'publish';
if ($year) {
$where_parts[] = 'YEAR(post_date) = %d';
$prepare_args[] = $year;
}
if ($month) {
$where_parts[] = 'MONTH(post_date) = %d';
$prepare_args[] = $month;
}
$ids_key = !empty($this->config['included']) ? 'included' : (!empty($this->config['excluded']) ? 'excluded' : null);
if ($ids_key) {
$ids = is_array($this->config[$ids_key]) ? $this->config[$ids_key] : explode(',', $this->config[$ids_key]);
$ids = array_map('intval', $ids);
$placeholders = implode(', ', array_fill(0, count($ids), '%d'));
$operator = $ids_key === 'included' ? 'IN' : 'NOT IN';
$where_parts[] = sprintf('%s.term_id %s (%s)', $wpdb->term_taxonomy, $operator, $placeholders);
$prepare_args = array_merge($prepare_args, $ids);
}
```
Explanation:
The old code directly embedded user-controlled values (like `$this->config['type']`, `$year`, `$month`, and category IDs) into the SQL string without proper escaping, making it vulnerable to SQL injection. The new code constructs an array of parameterized conditions (`$where_parts`) and corresponding arguments (`$prepare_args`), then uses `$wpdb->prepare()` to safely bind the arguments. Additionally, it sanitizes category IDs with `intval()`.
---
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - classes/class-jq-archive-list-datasource.php [Lines 159-161]
Old Code:
```php
return $wpdb->get_results( sprintf(
'SELECT DISTINCT ID, post_title, post_name, post_date, "false" as expanded FROM %s %s %s ORDER BY %s %s',
$wpdb->posts, $this->build_sql_join(), $this->build_sql_where( $year, $month ),
$this->query_val_to_field_name( $order_by[0] ),
$order_by[1]
) );
```
Fixed Code:
```php
list($where_clause, $where_args) = $this->build_sql_where( $year, $month);
$order_field = $this->query_val_to_field_name($order_by[0] ?? 'date');
$sql = "SELECT DISTINCT ID, post_title, post_name, post_date, 'false' as expanded FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause} ORDER BY {$order_field} {$order_direction}";
$results = $wpdb->get_results($wpdb->prepare($sql, ...$where_args));
return is_array($results) ? $results : null;
```
Explanation:
The old code used `sprintf()` to directly insert user-controlled values (from `$this->build_sql_where()` and `$order_by[1]`) into the SQL string, which could allow SQL injection. The new code uses parameterized queries via `$wpdb->prepare()` for the WHERE clause and validates `$order_direction` to ensure it's only 'ASC' or 'DESC', preventing injection through the ORDER BY clause.
---
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - classes/class-jq-archive-list-datasource.php [Lines 204-210]
Old Code:
```php
return $wpdb->get_results( sprintf(
'SELECT JAL.year, JAL.month, COUNT(JAL.ID) as `posts` FROM (
SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`,ID FROM %s %s %s)
JAL GROUP BY JAL.year, JAL.month ORDER BY JAL.year,JAL.month DESC',
$wpdb->posts, $this->build_sql_join(), $this->build_sql_where( $year )
) );
```
Fixed Code:
```php
list($where_clause, $where_args) = $this->build_sql_where(intval($year));
$sql = "SELECT JAL.year, JAL.month, COUNT(JAL.ID) as `posts` FROM (
SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, ID
FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause}) JAL
GROUP BY JAL.year, JAL.month ORDER BY JAL.year, JAL.month DESC";
return $wpdb->get_results($wpdb->prepare($sql, ...$where_args));
```
Explanation:
The old code concatenated the output of `$this->build_sql_where()` (which contained unsanitized user input) directly into the SQL string via `sprintf()`, creating an SQL injection risk. The new code uses `$wpdb->prepare()` with parameterized arguments to safely bind values, and also ensures `$year` is cast to an integer with `intval()` for additional safety.
CVE Analysis Results:
CVE-2025-54726: Yes
View CVE Description
Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') vulnerability in Miguel Useche JS Archive List allows SQL Injection. This issue affects JS Archive List: from n/a through n/a.
[CVE-2025-7670] [JS Archive List: 6.1.5→6.1.6] classes/class-jq-archive-list-datasource.php
AI: 5 vulnerabilities
5 true positives
CVE-2025-7670
--- cache/jquery-archive-list-widget_6.1.5/classes/class-jq-archive-list-datasource.php 2025-12-04 14:05:27.380736617 +0000+++ cache/jquery-archive-list-widget_6.1.6/classes/class-jq-archive-list-datasource.php 2025-12-04 14:05:29.716886575 +0000@@ -1,142 +1,186 @@ <?php+declare(strict_types=1); defined( 'ABSPATH' ) or die( 'No script kiddies please!' ); /** * Class to load required information from DB.+ *+ * @property array $config Configuration options for data source+ * @property bool $legacy Legacy mode flag */ class JQ_Archive_List_DataSource {- public $config;- public $legacy;-- public function __construct( $config = [], $legacy = false ) {- $this->config = $config;- $this->legacy = $legacy;- }-- public function get_years() {- global $wpdb;- $sql = sprintf(- 'SELECT JAL.year, COUNT(JAL.ID) as `posts` FROM (- SELECT DISTINCT YEAR(post_date) AS `year`, ID- FROM %s %s %s) JAL- GROUP BY JAL.year ORDER BY JAL.year DESC',- $wpdb->posts,- $this->build_sql_join(),- $this->build_sql_where()- );-- return $wpdb->get_results( $sql );- }-- protected function build_sql_join() {- global $wpdb;-- $join = '';-- if ( $this->has_filtering_categories() || $this->only_show_cur_category() ) {- $join = sprintf( ' LEFT JOIN %s ON(%s.ID = %s.object_id)',- $wpdb->term_relationships, $wpdb->posts, $wpdb->term_relationships- );-- $join .= sprintf( ' LEFT JOIN %s ON(%s.term_taxonomy_id = %s.term_taxonomy_id)',- $wpdb->term_taxonomy, $wpdb->term_relationships, $wpdb->term_taxonomy- );- }-- return apply_filters( 'getarchives_join', $join, [] );- }-- /**- * Check if user selected categories for inclusion or exclusion.- *- * @return bool If there are saved categories for including/excluding.- */- private function has_filtering_categories() {- return ! empty( $this->config['included'] ) || ! empty( $this->config['excluded'] );- }-- /**- * Returns if the option to show only current categories- * was selected and current page is a category page.- *- * @return bool Show only current category.- */- private function only_show_cur_category() {- return ! empty( $this->config['onlycategory'] );- }-- protected function build_sql_where( $year = null, $month = null ) {- global $wpdb;-- $where = sprintf( 'WHERE post_title != \'\' AND post_type = \'%s\' AND post_status = \'publish\' ', $this->config['type'] );-- if ( $year ) {- $where .= sprintf( 'AND YEAR(post_date) = %s ', $year );- }-- if ( $month ) {- $where .= sprintf( 'AND MONTH(post_date) = %s ', $month );- }-- if ( ! empty( $this->config['included'] ) ) {- $ids = is_array( $this->config['included'] ) ? implode( ',', $this->config['included'] ) : $this->config['included'];- $where .= sprintf( 'AND %s.term_id IN (%s)', $wpdb->term_taxonomy, $ids );- } elseif ( ! empty( $this->config['excluded'] ) ) {- $ids = is_array( $this->config['excluded'] ) ? implode( ',', $this->config['excluded'] ) : $this->config['excluded'];- $where .= sprintf( 'AND %s.term_id NOT IN (%s)', $wpdb->term_taxonomy, $ids );- }-- if ( $this->only_show_cur_category() ) {- // Leave config when removing legacy code.- $query_cat = get_query_var( 'cat' );- $categories_ids = empty( $query_cat ) ? $this->config['onlycategory'] : $query_cat;-- if ( ( $this->legacy && is_category() || ! $this->legacy ) ) {- $where .= sprintf( 'AND %s.term_id IN (%s) ', $wpdb->term_taxonomy, $categories_ids );- }- }-- if ( $this->has_filtering_categories() || $this->only_show_cur_category() ) {- $where .= 'AND ' . $wpdb->term_taxonomy . '.taxonomy=\'category\' ';- }-- return apply_filters( 'getarchives_where', $where, [] );- }-- public function get_posts( $year, $month ) {- global $wpdb;-- if ( empty( $year ) || empty( $month ) ) {- return null;- }-- $sort = isset( $this->config['sort'] ) ? $this->config['sort'] : 'date_desc';- $order_by = explode( '_', $sort );-- return $wpdb->get_results( sprintf(- 'SELECT DISTINCT ID, post_title, post_name, post_date, "false" as expanded FROM %s %s %s ORDER BY %s %s',- $wpdb->posts, $this->build_sql_join(), $this->build_sql_where( $year, $month ),- $this->query_val_to_field_name( $order_by[0] ),- $order_by[1]- ) );- }-- /**- * Maps the query value to the correct DB field.- *- * @param string $query_value received field name from the frontend.- *- * @return string DB field name.- */- private function query_val_to_field_name( $query_value ): string {- $map = [- 'id' => 'ID',- 'name' => 'post_title',- 'date' => 'post_date',- ];-- return $map[ $query_value ];- }+ /**+ * @var array Configuration options+ */+ private $config;+ /**+ * @var bool Legacy mode flag+ */+ private $legacy;++ /**+ * Constructor+ *+ * @param array $config+ * @param bool $legacy+ */+ public function __construct(array $config = [], bool $legacy = false) {+ $this->config = $config;+ $this->legacy = $legacy;+ }++ /**+ * Get years and post counts+ *+ * @return array|null+ */+ public function get_years(): ?array {+ global $wpdb;++ list($where_clause, $where_args) = $this->build_sql_where();++ $sql = "SELECT JAL.year, COUNT(JAL.ID) as `posts` FROM (+ SELECT DISTINCT YEAR(post_date) AS `year`, ID+ FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause}) JAL+ GROUP BY JAL.year ORDER BY JAL.year DESC";++ $results = $wpdb->get_results($wpdb->prepare($sql, ...$where_args));+ return is_array($results) ? $results : null;+ }++ /**+ * Build SQL JOIN clause+ *+ * @return string+ */+ protected function build_sql_join(): string {+ global $wpdb;+ $join = '';+ if ($this->has_filtering_categories() || $this->only_show_cur_category()) {+ $join = sprintf(' LEFT JOIN %s ON(%s.ID = %s.object_id)',+ $wpdb->term_relationships, $wpdb->posts, $wpdb->term_relationships+ );+ $join .= sprintf(' LEFT JOIN %s ON(%s.term_taxonomy_id = %s.term_taxonomy_id)',+ $wpdb->term_taxonomy, $wpdb->term_relationships, $wpdb->term_taxonomy+ );+ }+ return apply_filters('getarchives_join', $join, []);+ }++ /**+ * Check if user selected categories for inclusion or exclusion.+ *+ * @return bool+ */+ private function has_filtering_categories(): bool {+ return !empty($this->config['included']) || !empty($this->config['excluded']);+ }++ /**+ * Returns if the option to show only current categories was selected and current page is a category page.+ *+ * @return bool+ */+ private function only_show_cur_category(): bool {+ return !empty($this->config['onlycategory']);+ }++ /**+ * Build SQL WHERE clause+ *+ * @param int|null $year+ * @param int|null $month+ * @return array+ */+ protected function build_sql_where(?int $year = null, ?int $month = null): array {+ global $wpdb;+ $where_parts = [];+ $prepare_args = [];+ $where_parts[] = 'post_title != %s';+ $prepare_args[] = '';+ $where_parts[] = 'post_type = %s';+ $prepare_args[] = $this->config['type'] ?? 'post';+ $where_parts[] = 'post_status = %s';+ $prepare_args[] = 'publish';+ if ($year) {+ $where_parts[] = 'YEAR(post_date) = %d';+ $prepare_args[] = $year;+ }+ if ($month) {+ $where_parts[] = 'MONTH(post_date) = %d';+ $prepare_args[] = $month;+ }+ $ids_key = !empty($this->config['included']) ? 'included' : (!empty($this->config['excluded']) ? 'excluded' : null);+ if ($ids_key) {+ $ids = is_array($this->config[$ids_key]) ? $this->config[$ids_key] : explode(',', $this->config[$ids_key]);+ $ids = array_map('intval', $ids);+ $placeholders = implode(', ', array_fill(0, count($ids), '%d'));+ $operator = $ids_key === 'included' ? 'IN' : 'NOT IN';+ $where_parts[] = sprintf('%s.term_id %s (%s)', $wpdb->term_taxonomy, $operator, $placeholders);+ $prepare_args = array_merge($prepare_args, $ids);+ }+ if ($this->only_show_cur_category()) {+ $query_cat = get_query_var('cat');+ $categories_ids = empty($query_cat) ? $this->config['onlycategory'] : $query_cat;+ $categories_ids = is_array($categories_ids) ? $categories_ids : explode(',', $categories_ids);+ $categories_ids = array_map('intval', $categories_ids);+ if (($this->legacy && is_category()) || !$this->legacy) {+ $placeholders = implode(', ', array_fill(0, count($categories_ids), '%d'));+ $where_parts[] = sprintf('%s.term_id IN (%s)', $wpdb->term_taxonomy, $placeholders);+ $prepare_args = array_merge($prepare_args, $categories_ids);+ }+ }+ if ($this->has_filtering_categories() || $this->only_show_cur_category()) {+ $where_parts[] = $wpdb->term_taxonomy . '.taxonomy=%s';+ $prepare_args[] = 'category';+ }+ $where_clause_fragment = implode(' AND ', $where_parts);+ $where_clause_fragment = apply_filters('getarchives_where', $where_clause_fragment, []);+ return [$where_clause_fragment, $prepare_args];+ }++ /**+ * Get posts for a given year and month+ *+ * @param int $year+ * @param int $month+ * @return array|null+ */+ public function get_posts(int $year, int $month): ?array {+ global $wpdb;+ if ($year <= 0 || $month <= 0 || $month > 12) {+ return null;+ }+ $sort = $this->config['sort'] ?? 'date_desc';+ $order_by = explode('_', $sort);+ $order_direction = strtoupper($order_by[1] ?? 'DESC');+ if (!in_array($order_direction, ['ASC', 'DESC'], true)) {+ $order_direction = 'DESC';+ }+ list($where_clause, $where_args) = $this->build_sql_where( $year, $month);+ $order_field = $this->query_val_to_field_name($order_by[0] ?? 'date');+ $sql = "SELECT DISTINCT ID, post_title, post_name, post_date, 'false' as expanded FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause} ORDER BY {$order_field} {$order_direction}";+ $results = $wpdb->get_results($wpdb->prepare($sql, ...$where_args));+ return is_array($results) ? $results : null;+ }++ /**+ * Maps the query value to the correct DB field.+ *+ * @param string $query_value+ * @return string+ */+ protected function query_val_to_field_name(string $query_value): string {+ switch ($query_value) {+ case 'title':+ return 'post_title';+ case 'name':+ return 'post_name';+ case 'date':+ default:+ return 'post_date';+ }+ } public function year_should_be_expanded( $year, $cur_post_year, $cur_post_month, $expandConfig ): bool { $months = $this->get_months( $year );@@ -160,13 +204,13 @@ public function get_months( $year ) { global $wpdb;+ list($where_clause, $where_args) = $this->build_sql_where(intval($year));+ $sql = "SELECT JAL.year, JAL.month, COUNT(JAL.ID) as `posts` FROM (+ SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, ID+ FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause}) JAL+ GROUP BY JAL.year, JAL.month ORDER BY JAL.year, JAL.month DESC"; - return $wpdb->get_results( sprintf(- 'SELECT JAL.year, JAL.month, COUNT(JAL.ID) as `posts` FROM (- SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`,ID FROM %s %s %s)- JAL GROUP BY JAL.year, JAL.month ORDER BY JAL.year,JAL.month DESC',- $wpdb->posts, $this->build_sql_join(), $this->build_sql_where( $year )- ) );+ return $wpdb->get_results($wpdb->prepare($sql, ...$where_args)); } }
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - classes/class-jq-archive-list-datasource.php Lines 36-44 (old), Lines 39-47 (new)
Old Code:
```php
$sql = sprintf(
'SELECT JAL.year, COUNT(JAL.ID) as `posts` FROM (
SELECT DISTINCT YEAR(post_date) AS `year`, ID
FROM %s %s %s) JAL
GROUP BY JAL.year ORDER BY JAL.year DESC',
$wpdb->posts,
$this->build_sql_join(),
$this->build_sql_where()
);
return $wpdb->get_results( $sql );
```
Fixed Code:
```php
list($where_clause, $where_args) = $this->build_sql_where();
$sql = "SELECT JAL.year, COUNT(JAL.ID) as `posts` FROM (
SELECT DISTINCT YEAR(post_date) AS `year`, ID
FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause}) JAL
GROUP BY JAL.year ORDER BY JAL.year DESC";
$results = $wpdb->get_results($wpdb->prepare($sql, ...$where_args));
return is_array($results) ? $results : null;
```
Explanation:
The old code directly concatenated user-controlled input (from `$this->build_sql_where()`) into SQL queries without proper parameterization, creating SQL injection vulnerabilities. The fixed code uses `$wpdb->prepare()` with parameterized queries, ensuring proper escaping and neutralization of special elements.
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - classes/class-jq-archive-list-datasource.php Lines 94-98 (old), Lines 112-127 (new)
Old Code:
```php
if (! empty( $this->config['included'] ) ) {
$ids = is_array( $this->config['included'] ) ? implode( ',', $this->config['included'] ) : $this->config['included'];
$where .= sprintf( 'AND %s.term_id IN (%s)', $wpdb->term_taxonomy, $ids );
} elseif ( ! empty( $this->config['excluded'] ) ) {
$ids = is_array( $this->config['excluded'] ) ? implode( ',', $this->config['excluded'] ) : $this->config['excluded'];
$where .= sprintf( 'AND %s.term_id NOT IN (%s)', $wpdb->term_taxonomy, $ids );
}
```
Fixed Code:
```php
$ids_key = !empty($this->config['included']) ? 'included' : (!empty($this->config['excluded']) ? 'excluded' : null);
if ($ids_key) {
$ids = is_array($this->config[$ids_key]) ? $this->config[$ids_key] : explode(',', $this->config[$ids_key]);
$ids = array_map('intval', $ids);
$placeholders = implode(', ', array_fill(0, count($ids), '%d'));
$operator = $ids_key === 'included' ? 'IN' : 'NOT IN';
$where_parts[] = sprintf('%s.term_id %s (%s)', $wpdb->term_taxonomy, $operator, $placeholders);
$prepare_args = array_merge($prepare_args, $ids);
}
```
Explanation:
The old code directly embedded user-controlled category IDs into SQL queries without validation or escaping. The fixed code validates and sanitizes IDs using `intval()`, creates proper parameter placeholders, and passes values through `$wpdb->prepare()` for safe query execution.
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - classes/class-jq-archive-list-datasource.php Lines 100-107 (old), Lines 128-136 (new)
Old Code:
```php
if ( $this->only_show_cur_category() ) {
$query_cat = get_query_var( 'cat' );
$categories_ids = empty( $query_cat ) ? $this->config['onlycategory'] : $query_cat;
if ( ( $this->legacy && is_category() || ! $this->legacy ) ) {
$where .= sprintf( 'AND %s.term_id IN (%s) ', $wpdb->term_taxonomy, $categories_ids );
}
}
```
Fixed Code:
```php
if ($this->only_show_cur_category()) {
$query_cat = get_query_var('cat');
$categories_ids = empty($query_cat) ? $this->config['onlycategory'] : $query_cat;
$categories_ids = is_array($categories_ids) ? $categories_ids : explode(',', $categories_ids);
$categories_ids = array_map('intval', $categories_ids);
if (($this->legacy && is_category()) || !$this->legacy) {
$placeholders = implode(', ', array_fill(0, count($categories_ids), '%d'));
$where_parts[] = sprintf('%s.term_id IN (%s)', $wpdb->term_taxonomy, $placeholders);
$prepare_args = array_merge($prepare_args, $categories_ids);
}
}
```
Explanation:
The old code directly inserted category IDs from query variables or configuration into SQL queries without sanitization. The fixed code validates and sanitizes IDs using `intval()` and uses parameterized queries to prevent SQL injection attacks.
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - classes/class-jq-archive-list-datasource.php Lines 117-125 (old), Lines 159-162 (new)
Old Code:
```php
return $wpdb->get_results( sprintf(
'SELECT DISTINCT ID, post_title, post_name, post_date, "false" as expanded FROM %s %s %s ORDER BY %s %s',
$wpdb->posts, $this->build_sql_join(), $this->build_sql_where( $year, $month ),
$this->query_val_to_field_name( $order_by[0] ),
$order_by[1]
) );
```
Fixed Code:
```php
list($where_clause, $where_args) = $this->build_sql_where( $year, $month);
$order_field = $this->query_val_to_field_name($order_by[0] ?? 'date');
$sql = "SELECT DISTINCT ID, post_title, post_name, post_date, 'false' as expanded FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause} ORDER BY {$order_field} {$order_direction}";
$results = $wpdb->get_results($wpdb->prepare($sql, ...$where_args));
return is_array($results) ? $results : null;
```
Explanation:
The old code concatenated user-controlled `$order_by[1]` (sort direction) directly into the SQL query without validation, allowing potential SQL injection. The fixed code validates the order direction against a whitelist (`['ASC', 'DESC']`) and uses parameterized queries for the WHERE clause.
Vulnerability Existed: yes
TRUE POSITIVE
CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') - classes/class-jq-archive-list-datasource.php Lines 204-210 (old), Lines 207-213 (new)
Old Code:
```php
return $wpdb->get_results( sprintf(
'SELECT JAL.year, JAL.month, COUNT(JAL.ID) as `posts` FROM (
SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`,ID FROM %s %s %s)
JAL GROUP BY JAL.year, JAL.month ORDER BY JAL.year,JAL.month DESC',
$wpdb->posts, $this->build_sql_join(), $this->build_sql_where( $year )
) );
```
Fixed Code:
```php
list($where_clause, $where_args) = $this->build_sql_where(intval($year));
$sql = "SELECT JAL.year, JAL.month, COUNT(JAL.ID) as `posts` FROM (
SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, ID
FROM {$wpdb->posts} {$this->build_sql_join()} WHERE {$where_clause}) JAL
GROUP BY JAL.year, JAL.month ORDER BY JAL.year, JAL.month DESC";
return $wpdb->get_results($wpdb->prepare($sql, ...$where_args));
```
Explanation:
The old code suffered from the same SQL injection vulnerability in the `get_months()` method by directly concatenating user input. The fixed code uses parameterized queries with `$wpdb->prepare()` to safely handle the WHERE clause parameters.
CVE Analysis Results:
CVE-2025-7670: Yes
View CVE Description
The JS Archive List plugin for WordPress is vulnerable to time-based SQL Injection via the build_sql_where() function in all versions up to, and including, 6.1.5 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
[CVE-2025-7670] [JS Archive List: 6.1.5→6.1.6] jquery-archive-list-widget.php
AI: Not Sure
CVE-2025-7670
--- cache/jquery-archive-list-widget_6.1.5/jquery-archive-list-widget.php 2025-12-04 14:05:27.384736873 +0000+++ cache/jquery-archive-list-widget_6.1.6/jquery-archive-list-widget.php 2025-12-04 14:05:29.716886575 +0000@@ -3,7 +3,7 @@ Plugin Name: JS Archive List Plugin URI: http://skatox.com/blog/jquery-archive-list-widget/ Description: A widget for displaying an archive list with some effects.- Version: 6.1.4+ Version: 6.1.6 Author: Miguel Angel Useche Castro Author URI: https://migueluseche.com/ Text Domain: jalw_i18n@@ -35,7 +35,7 @@ define( 'JAL_BASE_URL', plugin_dir_url( __FILE__ ) ); } if ( ! defined( 'JAL_VERSION' ) ) {- define( 'JAL_VERSION', '6.1.4' );+ define( 'JAL_VERSION', '6.1.6' ); } require_once( 'admin/class-jaw-walker-category-checklist.php' );
AI Analysis
Vulnerability Existed: not sure CWE-200: Exposure of Sensitive Information to an Unauthorized Actor - jquery-archive-list-widget.php [3, 35] Old Code: ```php Version: 6.1.4 ``` ```php define( 'JAL_VERSION', '6.1.4' ); ``` Fixed Code: ```php Version: 6.1.6 ``` ```php define( 'JAL_VERSION', '6.1.6' ); ``` Explanation: The diff only shows a version number update from 6.1.4 to 6.1.6. Without additional context from the full code or changelog, it's impossible to determine if this version bump includes security fixes. The version number itself could indicate security patches were applied, but the diff alone doesn't reveal any vulnerability details. Therefore, I'm uncertain whether a vulnerability existed in the previous version.
CVE Analysis Results:
CVE-2025-7670: Yes
View CVE Description
The JS Archive List plugin for WordPress is vulnerable to time-based SQL Injection via the build_sql_where() function in all versions up to, and including, 6.1.5 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
Showing 21 to 26 of 26 results