Skip to content

Commit 619f108

Browse files
authored
Merge pull request #429 from bytepattern/feature/vulnerability_checker_wordfence
Feature/vulnerability checker wordfence - PR 419
2 parents 019b171 + 6bdf406 commit 619f108

File tree

1 file changed

+170
-26
lines changed

1 file changed

+170
-26
lines changed

class/class-mainwp-child-vulnerability-checker.php

Lines changed: 170 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ class MainWP_Child_Vulnerability_Checker {
4848
*/
4949
private $wpvulndb_nvd_api = 'https://services.nvd.nist.gov/rest/json/cves/2.0';
5050

51+
/**
52+
* Wordfence API URL
53+
*/
54+
private $wordfence_api_url = 'www.wordfence.com/api/intelligence/v2/vulnerabilities/';
55+
5156

5257
/**
5358
* Method instance()
@@ -100,7 +105,7 @@ public function vulner_recheck() {
100105
$result = array();
101106
// phpcs:disable WordPress.Security.NonceVerification
102107
$force = ( isset( $_POST['force'] ) && ! empty( $_POST['force'] ) ) ? true : false;
103-
$service = ( isset( $_POST['service'] ) && 'nvd_nist' === $_POST['service'] ) ? 'nvd_nist' : 'wpvulndb';
108+
$service = ( isset( $_POST['service'] ) && 'nvd_nist' === $_POST['service'] ) ? 'nvd_nist' : ( ( isset( $_POST['service'] ) && 'wordfence' == $_POST['service'] ) ? 'wordfence' : 'wpvulndb' );
104109

105110
if ( 'wpvulndb' === $service ) {
106111
if ( empty( $_POST['wpvulndb_tk'] ) ) {
@@ -120,6 +125,87 @@ public function vulner_recheck() {
120125
);
121126
}
122127

128+
/**
129+
* @param $version
130+
* @param $data
131+
* @return bool
132+
*/
133+
function isFixedVersion( $version, $data ) {
134+
if ( $data['from_version'] === '*' && $data['to_version'] === '*' ) {
135+
return false;
136+
}
137+
138+
if ( $data['from_version'] === '*' ) {
139+
$data['from_version'] = '0';
140+
}
141+
142+
$fromOperator = $data['from_inclusive'] ? '>=' : '>';
143+
$toOperator = $data['to_inclusive'] ? '<=' : '<';
144+
145+
if ( version_compare ($version, $data['from_version'], $fromOperator) && version_compare ($version, $data['to_version'], $toOperator) ) {
146+
return false;
147+
} else {
148+
return true;
149+
}
150+
}
151+
152+
/**
153+
* @param $data
154+
* @param $slug
155+
* @param $version
156+
* @return array
157+
*/
158+
public function get_vuln_wordfence_info( $data, $slug, $version ) {
159+
160+
$data = json_decode( $data, true );
161+
$filtered_data = array();
162+
163+
if ( is_array( $data ) && isset( $data['result'] ) && isset( $data['result']['CVE_Items'] ) ) {
164+
$vulns = array();
165+
$version_missed = true;
166+
$fixed = false;
167+
168+
foreach ( $data['result']['CVE_Items'] as $item ) {
169+
$info = array();
170+
$remediation = '';
171+
foreach ( $item['software'] as $software ) {
172+
foreach ( $software['affected_versions'] as $affected_version ) {
173+
$fixed = $this->isFixedVersion($version, $affected_version);
174+
$version_missed = false;
175+
if ( $fixed ) {
176+
break;
177+
}
178+
}
179+
180+
$remediation .= ' [' . ucfirst($software['type']) . '->' . $software['name'] . '] ' . $software['remediation'];
181+
}
182+
183+
if ( ! $fixed ) {
184+
if ( isset( $item['published'] ) ) {
185+
$info['date'] = $item['published'];
186+
}
187+
188+
if ( isset( $item['description'] ) ) {
189+
$info['detail'] = $item['description'] . $remediation;
190+
}
191+
192+
$customCveId = explode( '-', $item['id']);
193+
$info['cve_id'] = $item['cve'] ?? end($customCveId);
194+
$info['slug'] = $slug;
195+
}
196+
if ( $version_missed ) {
197+
$info['missed_version'] = 1;
198+
}
199+
if ( ! empty( $info ) ) {
200+
$vulns[] = $info;
201+
}
202+
}
203+
$filtered_data[ $slug ]['vulnerabilities'] = $vulns;
204+
}
205+
206+
return $filtered_data;
207+
}
208+
123209
/**
124210
* Check for plugin vulnerabilities.
125211
*
@@ -151,6 +237,8 @@ public function check_plugins( $force = false, $service = '' ) { //phpcs:ignore
151237
}
152238
// URL Syntax example: https://services.nvd.nist.gov/rest/json/cves/2.0?virtualMatchString=cpe:2.3:a:automattic:akismet:* .
153239
$url = $this->wpvulndb_nvd_api . '?virtualMatchString=cpe:2.3:a:' . $author_name . ':' . $slug . ':*';
240+
} elseif ( 'wordfence' === $service ) {
241+
$url = $this->wordfence_api_url . 'production?keyword=' . $string[0];
154242
} else {
155243
$url = $this->wpvulndb_api . 'plugins/' . $string[0];
156244
}
@@ -161,6 +249,8 @@ public function check_plugins( $force = false, $service = '' ) { //phpcs:ignore
161249
$plug_vuln_filter = array();
162250
if ( 'nvd_nist' === $service ) {
163251
$plug_vuln_filter = $this->get_vuln_nvd_nist_info( $plug_vuln, $string[0], $plugin_version );
252+
} elseif ( 'wordfence' == $service ) {
253+
$plug_vuln_filter = $this->get_vuln_wordfence_info( $plug_vuln, $string[0], $plugin_version );
164254
} else {
165255
$plug_vuln = json_decode( $plug_vuln, true );
166256
$plug_vuln_filter = $plug_vuln;
@@ -214,6 +304,8 @@ public function check_wp( $force = false, $service = '' ) {
214304
if ( false === $wp_vuln || $force ) {
215305
if ( 'nvd_nist' === $service ) {
216306
$url = $this->wpvulndb_nvd_api . '?virtualMatchString=cpe:2.3:a:WordPress:WordPress';
307+
} elseif ( 'wordfence' == $service ) {
308+
$url = $this->wordfence_api_url . 'production?keyword=WordPress';
217309
} else {
218310
$url = $this->wpvulndb_api . 'wordpresses/' . $number_version;
219311
}
@@ -223,6 +315,12 @@ public function check_wp( $force = false, $service = '' ) {
223315
$wp_vuln = $this->get_vuln_nvd_nist_info( $wp_vuln, 'wordpress', $wp_version ); //phpcs:ignore -- wordpress.
224316
$wp_vuln = wp_json_encode( $wp_vuln );
225317
}
318+
319+
if ( 'wordfence' == $service ) {
320+
$wp_vuln = $this->get_vuln_wordfence_info( $wp_vuln, 'WordPress', $wp_version );
321+
$wp_vuln = wp_json_encode( $wp_vuln );
322+
}
323+
226324
set_transient( 'mainwp_vulnche_trans_wp_json', $wp_vuln, 1 * DAY_IN_SECONDS );
227325
}
228326
return $wp_vuln;
@@ -257,6 +355,8 @@ public function check_themes( $force = false, $service = '' ) { // phpcs:ignore
257355

258356
if ( 'nvd_nist' === $service ) {
259357
$url = $this->wpvulndb_nvd_api . '?keywordSearch=' . $th['id'] . '&keywordExactMatch';
358+
} elseif ( 'wordfence' === $service ) {
359+
$url = $this->wordfence_api_url . 'production?keyword=' . $th['id'];
260360
} else {
261361
$url = $this->wpvulndb_api . 'themes/' . $th['id'];
262362
}
@@ -268,6 +368,8 @@ public function check_themes( $force = false, $service = '' ) { // phpcs:ignore
268368
if ( $th_vuln ) {
269369
if ( 'nvd_nist' === $service ) {
270370
$th_vuln_filter = $this->get_vuln_nvd_nist_info( $th_vuln, $th['id'], $th['version'] );
371+
} elseif ( 'wordfence' === $service ) {
372+
$th_vuln_filter = $this->get_vuln_wordfence_info( $th_vuln, $th['id'], $th['version'] );
271373
} else {
272374
$th_vuln = json_decode( $th_vuln, true );
273375
$th_vuln_filter = $th_vuln;
@@ -535,37 +637,79 @@ public function get_vuln_nvd_nist_info( $data, $slug, $version ) { // phpcs:ign
535637
*/
536638
public function vulnche_get_content( $url, $service = '' ) {
537639

538-
$encrypted = get_transient( 'mainwp_child_trans_wpvulndb_tk' );
539-
$wpvulndb_token = MainWP_Child_Keys_Manager::instance()->decrypt_string( $encrypted );
540-
541-
// phpcs:disable WordPress.WP.AlternativeFunctions -- Required to achieve desired results, pull request solutions appreciated.
542-
$ch = curl_init();
543-
curl_setopt( $ch, CURLOPT_URL, $url );
544-
curl_setopt( $ch, CURLOPT_HEADER, 0 );
545-
if ( 'nvd_nist' !== $service ) {
546-
if ( empty( $wpvulndb_token ) ) {
547-
if ( 'resource' === gettype( $ch ) ) {
548-
curl_close( $ch );
640+
if ( 'wordfence' === $service ) {
641+
642+
$typeName = explode( '=', $url );
643+
if ( $this->wordfenceJson == '' ) {
644+
$ch = curl_init();
645+
curl_setopt( $ch, CURLOPT_URL, $url );
646+
curl_setopt( $ch, CURLOPT_HEADER, 0 );
647+
curl_setopt( $ch, CURLOPT_USERAGENT, $this->get_random_user_agent() );
648+
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
649+
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
650+
$response = curl_exec( $ch );
651+
$this->wordfenceJson = json_decode($response, true);
652+
}
653+
$output = array();
654+
$resultData = array();
655+
foreach ( $this->wordfenceJson as $feed ) {
656+
if ( $typeName[1] == $feed['software'][0]['slug'] ) {
657+
$output[] = $feed;
658+
659+
$resultData[] = array(
660+
'CVE_data_type' => isset($feed['cve']) ? 'CVE' : '',
661+
'references' => array( 'reference_data' => $feed['references'] ),
662+
'description' => array( 'description_data' => $feed['description'] ),
663+
'publishedDate' => $feed['published'],
664+
'lastModifiedDate' => $feed['updated'],
665+
);
549666
}
550-
return false;
551667
}
552-
curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Authorization: Token token=' . $wpvulndb_token ) );
553-
}
554-
curl_setopt( $ch, CURLOPT_USERAGENT, $this->get_random_user_agent() );
555-
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
556668

557-
$output = curl_exec( $ch );
558-
$info = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
669+
$data = array(
670+
'resultsPerPage' => count($output),
671+
'startIndex' => 0,
672+
'totalResults' => count($output),
673+
'result' => array(
674+
'CVE_Items' => $output,
675+
),
676+
);
559677

560-
if ( 'resource' === gettype( $ch ) ) {
561-
curl_close( $ch );
562-
}
563-
// phpcs:enable
678+
return json_encode($data);
679+
} else {
680+
681+
$encrypted = get_transient( 'mainwp_child_trans_wpvulndb_tk' );
682+
$wpvulndb_token = MainWP_Child_Keys_Manager::instance()->decrypt_string( $encrypted );
683+
684+
// phpcs:disable WordPress.WP.AlternativeFunctions -- Required to achieve desired results, pull request solutions appreciated.
685+
$ch = curl_init();
686+
curl_setopt( $ch, CURLOPT_URL, $url );
687+
curl_setopt( $ch, CURLOPT_HEADER, 0 );
688+
if ( 'nvd_nist' !== $service ) {
689+
if ( empty( $wpvulndb_token ) ) {
690+
if ( 'resource' === gettype( $ch ) ) {
691+
curl_close( $ch );
692+
}
693+
return false;
694+
}
695+
curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Authorization: Token token=' . $wpvulndb_token ) );
696+
}
697+
curl_setopt( $ch, CURLOPT_USERAGENT, $this->get_random_user_agent() );
698+
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
699+
700+
$output = curl_exec( $ch );
701+
$info = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
564702

565-
if ( false === $output || 200 !== (int) $info ) {
566-
$output = null;
703+
if ( 'resource' === gettype( $ch ) ) {
704+
curl_close( $ch );
705+
}
706+
// phpcs:enable
707+
708+
if ( false === $output || 200 !== (int) $info ) {
709+
$output = null;
710+
}
711+
return $output;
567712
}
568-
return $output;
569713
}
570714

571715
/**

0 commit comments

Comments
 (0)