PluginProbe
WPVulnerability / 5.1.6
WPVulnerability v5.1.6
5.1.6 5.1.2 5.1.1 5.0.1 5.0.0 trunk 0.1 0.2 1.0 1.0.1 1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 All 57 releases
wpvulnerability / wpvulnerability-api.php

wpvulnerability-api.php in WPVulnerability 5.1.6, at wpvulnerability-api.php

647 lines 29.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPVulnerability REST API Endpoints
4 *
5 * @package WPVulnerability
6 *
7 * @since 3.3.0
8 */
9
10 defined( 'ABSPATH' ) || die( 'No script kiddies please!' );
11
12 /**
13 * Handle the core vulnerabilities REST API request.
14 *
15 * This function handles the request for retrieving core vulnerabilities.
16 * It includes the necessary files and fetches the vulnerabilities data.
17 *
18 * @since 3.3.0
19 *
20 * @return WP_REST_Response Core vulnerabilities data or a message if none found.
21 */
22 function wpvulnerability_rest_core_vulnerabilities() {
23 // Include the files containing the functions to get core vulnerabilities.
24 require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-general.php';
25 require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-core.php';
26
27 // Get the core vulnerabilities.
28 $core_vulnerabilities = wpvulnerability_core_get_vulnerabilities();
29
30 $core_complete = array();
31
32 // Check if vulnerabilities are found and is an array.
33 if ( $core_vulnerabilities ) {
34 // Loop through each core vulnerability.
35 foreach ( $core_vulnerabilities as $vulnerability ) {
36 if ( ! is_array( $vulnerability ) ) {
37 continue; }
38 $vuln_impact = isset( $vulnerability['impact'] ) && is_array( $vulnerability['impact'] ) ? $vulnerability['impact'] : array();
39 $vuln_cvss = isset( $vuln_impact['cvss'] ) && is_array( $vuln_impact['cvss'] ) ? $vuln_impact['cvss'] : array();
40 $vuln_cvss3 = isset( $vuln_impact['cvss3'] ) && is_array( $vuln_impact['cvss3'] ) ? $vuln_impact['cvss3'] : array();
41 $vuln_ssvc = isset( $vuln_impact['ssvc'] ) && is_array( $vuln_impact['ssvc'] ) ? $vuln_impact['ssvc'] : array();
42 $vuln_cwe = isset( $vuln_impact['cwe'] ) && is_array( $vuln_impact['cwe'] ) ? $vuln_impact['cwe'] : array();
43 $vuln_sources = isset( $vulnerability['source'] ) && is_array( $vulnerability['source'] ) ? $vulnerability['source'] : array();
44
45 $core_complete_temp = array();
46
47 // UUID — unique vulnerability identifier.
48 $core_complete_temp['uuid'] = is_scalar( $vulnerability['uuid'] ?? '' ) ? (string) ( $vulnerability['uuid'] ?? '' ) : '';
49
50 // Process vulnerability version.
51 $core_complete_temp['version'] = trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability['name'] ?? null ) ? (string) $vulnerability['name'] : '' ), 'strip' ) ) );
52
53 // Process vulnerability severity — prefer cvss3 full-word value.
54 $cvss3_sev_raw = isset( $vuln_cvss3['severity'] ) ? $vuln_cvss3['severity'] : null;
55 $cvss3_sev = is_string( $cvss3_sev_raw ) && '' !== $cvss3_sev_raw ? $cvss3_sev_raw : null;
56 $cvss_sev = isset( $vuln_cvss['severity'] ) && is_scalar( $vuln_cvss['severity'] ) ? (string) $vuln_cvss['severity'] : null;
57 $sev_input = null !== $cvss3_sev ? $cvss3_sev : $cvss_sev;
58 $core_complete_temp['severity'] = null !== $sev_input ? wpvulnerability_severity( $sev_input ) : null;
59
60 // KEV flag — Known Exploited Vulnerability (CISA KEV catalog).
61 $core_complete_temp['kev'] = isset( $vuln_ssvc['kev'] ) && true === $vuln_ssvc['kev'];
62
63 // SSVC block.
64 if ( ! empty( $vuln_ssvc ) ) {
65 $core_complete_temp['ssvc'] = array(
66 'exploitation' => is_scalar( $vuln_ssvc['exploitation'] ?? '' ) ? (string) ( $vuln_ssvc['exploitation'] ?? '' ) : '',
67 'automatable' => is_scalar( $vuln_ssvc['automatable'] ?? '' ) ? (string) ( $vuln_ssvc['automatable'] ?? '' ) : '',
68 'technical_impact' => is_scalar( $vuln_ssvc['technical_impact'] ?? '' ) ? (string) ( $vuln_ssvc['technical_impact'] ?? '' ) : '',
69 );
70 } else {
71 $core_complete_temp['ssvc'] = null;
72 }
73
74 // Process CWE details.
75 $core_complete_temp['cwe'] = array();
76 foreach ( $vuln_cwe as $vulnerability_cwe ) {
77 if ( ! is_array( $vulnerability_cwe ) ) {
78 continue; }
79 $core_complete_temp['cwe'][] = array(
80 'name' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_cwe['name'] ?? null ) ? (string) $vulnerability_cwe['name'] : '' ), 'strip' ) ) ),
81 'description' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_cwe['description'] ?? null ) ? (string) $vulnerability_cwe['description'] : '' ), 'strip' ) ) ),
82 );
83 }
84
85 // Process CVSS score.
86 $core_complete_temp['score'] = isset( $vuln_cvss['score'] ) ? number_format( is_scalar( $vuln_cvss['score'] ) ? (float) $vuln_cvss['score'] : 0.0, 1, '.', '' ) : null;
87
88 // Process vulnerability sources.
89 $core_complete_temp['source'] = array();
90 foreach ( $vuln_sources as $vulnerability_source ) {
91 if ( ! is_array( $vulnerability_source ) ) {
92 continue; }
93 $core_complete_temp['source'][] = array(
94 'name' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_source['name'] ?? null ) ? (string) $vulnerability_source['name'] : '' ), 'strip' ) ) ),
95 'link' => esc_url_raw( ( is_scalar( $vulnerability_source['link'] ?? null ) ? (string) $vulnerability_source['link'] : '' ) ),
96 'description' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_source['description'] ?? null ) ? (string) $vulnerability_source['description'] : '' ), 'strip' ) ) ),
97 'date' => is_scalar( $vulnerability_source['date'] ?? '' ) ? (string) ( $vulnerability_source['date'] ?? '' ) : '',
98 );
99 }
100
101 $core_complete[] = $core_complete_temp;
102 unset( $core_complete_temp );
103 }
104 }
105
106 // Return the vulnerabilities in the response.
107 return new WP_REST_Response( $core_complete, 200 );
108 }
109
110 /**
111 * Handle the plugins vulnerabilities REST API request.
112 *
113 * This function handles the request for retrieving plugins vulnerabilities.
114 * It includes the necessary files and fetches the vulnerabilities data.
115 *
116 * @since 3.3.0
117 *
118 * @return WP_REST_Response Plugins vulnerabilities data or a message if none found.
119 */
120 function wpvulnerability_rest_plugins_vulnerabilities() {
121 // Include the files containing the functions to get plugins vulnerabilities.
122 require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-general.php';
123 require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-plugins.php';
124
125 // Get the plugins vulnerabilities.
126 $plugins_vulnerabilities = wpvulnerability_plugin_get_vulnerabilities();
127
128 $plugins_complete = array();
129
130 // Loop through each plugin vulnerability.
131 foreach ( $plugins_vulnerabilities as $plugin ) {
132 if ( ! is_array( $plugin ) ) {
133 continue; }
134 // Check if the plugin is vulnerable.
135 if ( 1 === ( is_scalar( $plugin['vulnerable'] ?? null ) ? (int) $plugin['vulnerable'] : 0 ) ) {
136 $plugins_complete_temp = array();
137
138 // Process plugin name and slug.
139 $plugins_complete_temp['name'] = trim( html_entity_decode( wp_kses( ( is_scalar( $plugin['Name'] ?? null ) ? (string) $plugin['Name'] : '' ), 'strip' ) ) );
140 $plugins_complete_temp['slug'] = trim( html_entity_decode( wp_kses( ( is_scalar( $plugin['slug'] ?? null ) ? (string) $plugin['slug'] : '' ), 'strip' ) ) );
141
142 // Prepare the vulnerabilities array for output.
143 $p_vulns = isset( $plugin['vulnerabilities'] ) && is_array( $plugin['vulnerabilities'] ) ? $plugin['vulnerabilities'] : array();
144 foreach ( $p_vulns as $vulnerability ) {
145 if ( ! is_array( $vulnerability ) ) {
146 continue; }
147 $vuln_impact = isset( $vulnerability['impact'] ) && is_array( $vulnerability['impact'] ) ? $vulnerability['impact'] : array();
148 $vuln_cvss = isset( $vuln_impact['cvss'] ) && is_array( $vuln_impact['cvss'] ) ? $vuln_impact['cvss'] : array();
149 $vuln_cvss3 = isset( $vuln_impact['cvss3'] ) && is_array( $vuln_impact['cvss3'] ) ? $vuln_impact['cvss3'] : array();
150 $vuln_ssvc = isset( $vuln_impact['ssvc'] ) && is_array( $vuln_impact['ssvc'] ) ? $vuln_impact['ssvc'] : array();
151 $vuln_cwe = isset( $vuln_impact['cwe'] ) && is_array( $vuln_impact['cwe'] ) ? $vuln_impact['cwe'] : array();
152 $vuln_sources = isset( $vulnerability['source'] ) && is_array( $vulnerability['source'] ) ? $vulnerability['source'] : array();
153
154 $plugins_complete_temp_vulnerabilities = array();
155
156 // UUID — unique vulnerability identifier.
157 $plugins_complete_temp_vulnerabilities['uuid'] = is_scalar( $vulnerability['uuid'] ?? '' ) ? (string) ( $vulnerability['uuid'] ?? '' ) : '';
158
159 // Process vulnerability severity — prefer cvss3 full-word value.
160 $cvss3_sev_raw = isset( $vuln_cvss3['severity'] ) ? $vuln_cvss3['severity'] : null;
161 $cvss3_sev = is_string( $cvss3_sev_raw ) && '' !== $cvss3_sev_raw ? $cvss3_sev_raw : null;
162 $cvss_sev = isset( $vuln_cvss['severity'] ) && is_scalar( $vuln_cvss['severity'] ) ? (string) $vuln_cvss['severity'] : null;
163 $sev_input = null !== $cvss3_sev ? $cvss3_sev : $cvss_sev;
164 $plugins_complete_temp_vulnerabilities['severity'] = null !== $sev_input ? wpvulnerability_severity( $sev_input ) : null;
165
166 // KEV flag — Known Exploited Vulnerability (CISA KEV catalog).
167 $plugins_complete_temp_vulnerabilities['kev'] = isset( $vuln_ssvc['kev'] ) && true === $vuln_ssvc['kev'];
168
169 // SSVC block.
170 if ( ! empty( $vuln_ssvc ) ) {
171 $plugins_complete_temp_vulnerabilities['ssvc'] = array(
172 'exploitation' => is_scalar( $vuln_ssvc['exploitation'] ?? '' ) ? (string) ( $vuln_ssvc['exploitation'] ?? '' ) : '',
173 'automatable' => is_scalar( $vuln_ssvc['automatable'] ?? '' ) ? (string) ( $vuln_ssvc['automatable'] ?? '' ) : '',
174 'technical_impact' => is_scalar( $vuln_ssvc['technical_impact'] ?? '' ) ? (string) ( $vuln_ssvc['technical_impact'] ?? '' ) : '',
175 );
176 } else {
177 $plugins_complete_temp_vulnerabilities['ssvc'] = null;
178 }
179
180 // Process vulnerability details.
181 $plugins_complete_temp_vulnerabilities['version'] = trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability['version'] ?? null ) ? (string) $vulnerability['version'] : '' ), 'strip' ) ) );
182 $plugins_complete_temp_vulnerabilities['affected'] = trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability['versions'] ?? null ) ? (string) $vulnerability['versions'] : '' ), 'strip' ) ) );
183 $plugins_complete_temp_vulnerabilities['name'] = trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability['name'] ?? null ) ? (string) $vulnerability['name'] : '' ), 'strip' ) ) );
184 $plugins_complete_temp_vulnerabilities['closed'] = is_numeric( $vulnerability['closed'] ?? 0 ) ? (int) ( $vulnerability['closed'] ?? 0 ) : 0;
185 $plugins_complete_temp_vulnerabilities['unfixed'] = is_numeric( $vulnerability['unfixed'] ?? 0 ) ? (int) ( $vulnerability['unfixed'] ?? 0 ) : 0;
186
187 // Process CWE details.
188 $plugins_complete_temp_vulnerabilities['cwe'] = array();
189 foreach ( $vuln_cwe as $vulnerability_cwe ) {
190 if ( ! is_array( $vulnerability_cwe ) ) {
191 continue; }
192 $plugins_complete_temp_vulnerabilities['cwe'][] = array(
193 'name' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_cwe['name'] ?? null ) ? (string) $vulnerability_cwe['name'] : '' ), 'strip' ) ) ),
194 'description' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_cwe['description'] ?? null ) ? (string) $vulnerability_cwe['description'] : '' ), 'strip' ) ) ),
195 );
196 }
197
198 // Process CVSS score.
199 $plugins_complete_temp_vulnerabilities['score'] = isset( $vuln_cvss['score'] ) ? number_format( is_scalar( $vuln_cvss['score'] ) ? (float) $vuln_cvss['score'] : 0.0, 1, '.', '' ) : null;
200
201 // Process vulnerability sources.
202 $plugins_complete_temp_vulnerabilities['source'] = array();
203 foreach ( $vuln_sources as $vulnerability_source ) {
204 if ( ! is_array( $vulnerability_source ) ) {
205 continue; }
206 $plugins_complete_temp_vulnerabilities['source'][] = array(
207 'name' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_source['name'] ?? null ) ? (string) $vulnerability_source['name'] : '' ), 'strip' ) ) ),
208 'link' => esc_url_raw( ( is_scalar( $vulnerability_source['link'] ?? null ) ? (string) $vulnerability_source['link'] : '' ) ),
209 'date' => is_scalar( $vulnerability_source['date'] ?? '' ) ? (string) ( $vulnerability_source['date'] ?? '' ) : '',
210 );
211 }
212
213 // Add processed vulnerability to the temporary array.
214 $plugins_complete_temp['vulnerabilities'][] = $plugins_complete_temp_vulnerabilities;
215 }
216
217 // Add processed plugin data to the complete array.
218 $plugins_complete[] = $plugins_complete_temp;
219 }
220 }
221
222 // Return the vulnerabilities in the response.
223 return new WP_REST_Response( $plugins_complete, 200 );
224 }
225
226 /**
227 * Handle the themes vulnerabilities REST API request.
228 *
229 * This function handles the request for retrieving themes vulnerabilities.
230 * It includes the necessary files and fetches the vulnerabilities data.
231 *
232 * @since 3.3.0
233 *
234 * @return WP_REST_Response Themes vulnerabilities data or a message if none found.
235 */
236 function wpvulnerability_rest_themes_vulnerabilities() {
237 // Include the file containing the function to get themes vulnerabilities.
238 require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-general.php';
239 require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-themes.php';
240
241 // Get the themes vulnerabilities.
242 $themes_vulnerabilities = wpvulnerability_theme_get_vulnerabilities();
243
244 $themes_complete = array();
245
246 // Loop through each theme vulnerability.
247 foreach ( $themes_vulnerabilities as $theme ) {
248 if ( ! is_array( $theme ) ) {
249 continue;
250 }
251 $theme_wpv = isset( $theme['wpvulnerability'] ) && is_array( $theme['wpvulnerability'] ) ? $theme['wpvulnerability'] : array();
252 // Check if the theme is vulnerable.
253 if ( 1 === ( is_numeric( $theme_wpv['vulnerable'] ?? 0 ) ? (int) ( $theme_wpv['vulnerable'] ?? 0 ) : 0 ) ) {
254 $themes_complete_temp = array();
255
256 // Process theme name and slug.
257 $themes_complete_temp['name'] = trim( html_entity_decode( wp_kses( ( is_scalar( $theme_wpv['name'] ?? null ) ? (string) $theme_wpv['name'] : '' ), 'strip' ) ) );
258 $themes_complete_temp['slug'] = trim( html_entity_decode( wp_kses( ( is_scalar( $theme_wpv['slug'] ?? null ) ? (string) $theme_wpv['slug'] : '' ), 'strip' ) ) );
259
260 // Prepare the vulnerabilities array for output.
261 $theme_vulns = isset( $theme_wpv['vulnerabilities'] ) && is_array( $theme_wpv['vulnerabilities'] ) ? $theme_wpv['vulnerabilities'] : array();
262 foreach ( $theme_vulns as $vulnerability ) {
263 if ( ! is_array( $vulnerability ) ) {
264 continue; }
265 $vuln_impact = isset( $vulnerability['impact'] ) && is_array( $vulnerability['impact'] ) ? $vulnerability['impact'] : array();
266 $vuln_cvss = isset( $vuln_impact['cvss'] ) && is_array( $vuln_impact['cvss'] ) ? $vuln_impact['cvss'] : array();
267 $vuln_cvss3 = isset( $vuln_impact['cvss3'] ) && is_array( $vuln_impact['cvss3'] ) ? $vuln_impact['cvss3'] : array();
268 $vuln_ssvc = isset( $vuln_impact['ssvc'] ) && is_array( $vuln_impact['ssvc'] ) ? $vuln_impact['ssvc'] : array();
269 $vuln_cwe = isset( $vuln_impact['cwe'] ) && is_array( $vuln_impact['cwe'] ) ? $vuln_impact['cwe'] : array();
270 $vuln_sources = isset( $vulnerability['source'] ) && is_array( $vulnerability['source'] ) ? $vulnerability['source'] : array();
271
272 $themes_complete_temp_vulnerabilities = array();
273
274 // UUID — unique vulnerability identifier.
275 $themes_complete_temp_vulnerabilities['uuid'] = is_scalar( $vulnerability['uuid'] ?? '' ) ? (string) ( $vulnerability['uuid'] ?? '' ) : '';
276
277 // Process vulnerability severity — prefer cvss3 full-word value.
278 $cvss3_sev_raw = isset( $vuln_cvss3['severity'] ) ? $vuln_cvss3['severity'] : null;
279 $cvss3_sev = is_string( $cvss3_sev_raw ) && '' !== $cvss3_sev_raw ? $cvss3_sev_raw : null;
280 $cvss_sev = isset( $vuln_cvss['severity'] ) && is_scalar( $vuln_cvss['severity'] ) ? (string) $vuln_cvss['severity'] : null;
281 $sev_input = null !== $cvss3_sev ? $cvss3_sev : $cvss_sev;
282 $themes_complete_temp_vulnerabilities['severity'] = null !== $sev_input ? wpvulnerability_severity( $sev_input ) : null;
283
284 // KEV flag — Known Exploited Vulnerability (CISA KEV catalog).
285 $themes_complete_temp_vulnerabilities['kev'] = isset( $vuln_ssvc['kev'] ) && true === $vuln_ssvc['kev'];
286
287 // SSVC block.
288 if ( ! empty( $vuln_ssvc ) ) {
289 $themes_complete_temp_vulnerabilities['ssvc'] = array(
290 'exploitation' => is_scalar( $vuln_ssvc['exploitation'] ?? '' ) ? (string) ( $vuln_ssvc['exploitation'] ?? '' ) : '',
291 'automatable' => is_scalar( $vuln_ssvc['automatable'] ?? '' ) ? (string) ( $vuln_ssvc['automatable'] ?? '' ) : '',
292 'technical_impact' => is_scalar( $vuln_ssvc['technical_impact'] ?? '' ) ? (string) ( $vuln_ssvc['technical_impact'] ?? '' ) : '',
293 );
294 } else {
295 $themes_complete_temp_vulnerabilities['ssvc'] = null;
296 }
297
298 // Process vulnerability details.
299 $themes_complete_temp_vulnerabilities['version'] = trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability['version'] ?? null ) ? (string) $vulnerability['version'] : '' ), 'strip' ) ) );
300 $themes_complete_temp_vulnerabilities['affected'] = trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability['versions'] ?? null ) ? (string) $vulnerability['versions'] : '' ), 'strip' ) ) );
301 $themes_complete_temp_vulnerabilities['name'] = trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability['name'] ?? null ) ? (string) $vulnerability['name'] : '' ), 'strip' ) ) );
302 $themes_complete_temp_vulnerabilities['closed'] = ( is_scalar( $vulnerability['closed'] ?? null ) ? (int) $vulnerability['closed'] : 0 );
303 $themes_complete_temp_vulnerabilities['unfixed'] = ( is_scalar( $vulnerability['unfixed'] ?? null ) ? (int) $vulnerability['unfixed'] : 0 );
304
305 // Process CWE details.
306 $themes_complete_temp_vulnerabilities['cwe'] = array();
307 foreach ( $vuln_cwe as $vulnerability_cwe ) {
308 if ( ! is_array( $vulnerability_cwe ) ) {
309 continue; }
310 $themes_complete_temp_vulnerabilities['cwe'][] = array(
311 'name' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_cwe['name'] ?? null ) ? (string) $vulnerability_cwe['name'] : '' ), 'strip' ) ) ),
312 'description' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_cwe['description'] ?? null ) ? (string) $vulnerability_cwe['description'] : '' ), 'strip' ) ) ),
313 );
314 }
315
316 // Process CVSS score.
317 $themes_complete_temp_vulnerabilities['score'] = isset( $vuln_cvss['score'] ) ? number_format( is_scalar( $vuln_cvss['score'] ) ? (float) $vuln_cvss['score'] : 0.0, 1, '.', '' ) : null;
318
319 // Process vulnerability sources.
320 $themes_complete_temp_vulnerabilities['source'] = array();
321 foreach ( $vuln_sources as $vulnerability_source ) {
322 if ( ! is_array( $vulnerability_source ) ) {
323 continue; }
324 $themes_complete_temp_vulnerabilities['source'][] = array(
325 'name' => trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability_source['name'] ?? null ) ? (string) $vulnerability_source['name'] : '' ), 'strip' ) ) ),
326 'link' => esc_url_raw( ( is_scalar( $vulnerability_source['link'] ?? null ) ? (string) $vulnerability_source['link'] : '' ) ),
327 'date' => is_scalar( $vulnerability_source['date'] ?? '' ) ? (string) ( $vulnerability_source['date'] ?? '' ) : '',
328 );
329 }
330
331 // Add processed vulnerability to the temporary array.
332 $themes_complete_temp['vulnerabilities'][] = $themes_complete_temp_vulnerabilities;
333 }
334
335 // Add processed theme data to the complete array.
336 $themes_complete[] = $themes_complete_temp;
337 }
338 }
339
340 // Return the vulnerabilities in the response.
341 return new WP_REST_Response( $themes_complete, 200 );
342 }
343
344 /**
345 * Handle vulnerabilities REST API request for different software types.
346 *
347 * This function processes the request to retrieve vulnerabilities for the specified software type.
348 * It loads the necessary files and fetches the vulnerability data, then returns the data in a structured format.
349 *
350 * @since 3.5.0
351 *
352 * @param string $software_type The type of software to retrieve vulnerabilities for.
353 * @return WP_REST_Response The vulnerabilities data or an empty array if none found.
354 */
355 function wpvulnerability_rest_software_vulnerabilities( $software_type ) {
356 // Include the general file for retrieving vulnerabilities.
357 require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-general.php';
358 require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-software.php';
359
360 // Get vulnerabilities based on the software type.
361 $software_data = array();
362 $vulnerabilities = array();
363 switch ( $software_type ) {
364 case 'php':
365 case 'apache':
366 case 'nginx':
367 case 'mariadb':
368 case 'mysql':
369 case 'imagemagick':
370 case 'curl':
371 case 'memcached':
372 case 'redis':
373 case 'sqlite':
374 $software_data = wpvulnerability_software_get_vulnerabilities( $software_type );
375 if ( is_array( $software_data ) && isset( $software_data['vulnerabilities'] ) && is_array( $software_data['vulnerabilities'] ) ) {
376 $vulnerabilities = $software_data['vulnerabilities'];
377 }
378 break;
379 default:
380 return new WP_REST_Response( array(), 400 ); // Invalid software type.
381 }
382
383 $complete_vulnerabilities = array();
384
385 // Process each vulnerability.
386 foreach ( $vulnerabilities as $vulnerability ) {
387 if ( ! is_array( $vulnerability ) ) {
388 continue; }
389 $sw_sources = isset( $vulnerability['source'] ) && is_array( $vulnerability['source'] ) ? $vulnerability['source'] : array();
390 $sw_impact = isset( $vulnerability['impact'] ) && is_array( $vulnerability['impact'] ) ? $vulnerability['impact'] : array();
391 $sw_cvss3 = isset( $sw_impact['cvss3'] ) && is_array( $sw_impact['cvss3'] ) ? $sw_impact['cvss3'] : array();
392 $raw_unfixed = $vulnerability['unfixed'] ?? 0;
393
394 $temp = array();
395 $temp['uuid'] = is_scalar( $vulnerability['uuid'] ?? '' ) ? (string) ( $vulnerability['uuid'] ?? '' ) : '';
396
397 // Severity from cvss3 if available.
398 $sw_cvss3_sev_raw = isset( $sw_cvss3['severity'] ) ? $sw_cvss3['severity'] : null;
399 $sw_cvss3_sev = is_string( $sw_cvss3_sev_raw ) && '' !== $sw_cvss3_sev_raw ? $sw_cvss3_sev_raw : null;
400 $temp['severity'] = null !== $sw_cvss3_sev ? wpvulnerability_severity( $sw_cvss3_sev ) : null;
401
402 // KEV flag — for software, kev is at impact level (not inside ssvc).
403 $temp['kev'] = isset( $sw_impact['kev'] ) && true === $sw_impact['kev'];
404
405 $temp['version'] = trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability['version'] ?? null ) ? (string) $vulnerability['version'] : '' ), 'strip' ) ) );
406 $temp['affected'] = trim( html_entity_decode( wp_kses( ( is_scalar( $vulnerability['versions'] ?? null ) ? (string) $vulnerability['versions'] : '' ), 'strip' ) ) );
407 $temp['unfixed'] = is_numeric( $raw_unfixed ) ? (int) $raw_unfixed : 0;
408
409 // Process vulnerability sources.
410 $temp['source'] = array();
411 foreach ( $sw_sources as $source ) {
412 if ( ! is_array( $source ) ) {
413 continue; }
414 $temp['source'][] = array(
415 'name' => trim( html_entity_decode( wp_kses( ( is_scalar( $source['id'] ?? null ) ? (string) $source['id'] : '' ), 'strip' ) ) ),
416 'description' => trim( html_entity_decode( wp_kses( ( is_scalar( $source['description'] ?? null ) ? (string) $source['description'] : '' ), 'strip' ) ) ),
417 'link' => esc_url_raw( ( is_scalar( $source['link'] ?? null ) ? (string) $source['link'] : '' ) ),
418 'date' => is_scalar( $source['date'] ?? '' ) ? (string) ( $source['date'] ?? '' ) : '',
419 );
420 }
421
422 // Add processed vulnerability to the complete array.
423 $complete_vulnerabilities[] = $temp;
424 }
425
426 // Return the vulnerabilities in the response.
427 return new WP_REST_Response( $complete_vulnerabilities, 200 );
428 }
429
430 /**
431 * Handle the PHP vulnerabilities REST API request.
432 *
433 * @since 3.3.0
434 *
435 * @return WP_REST_Response PHP vulnerabilities data or a message if none found.
436 */
437 function wpvulnerability_rest_php_vulnerabilities() {
438 return wpvulnerability_rest_software_vulnerabilities( 'php' );
439 }
440
441 /**
442 * Handle the Apache vulnerabilities REST API request.
443 *
444 * @since 3.3.0
445 *
446 * @return WP_REST_Response Apache vulnerabilities data or a message if none found.
447 */
448 function wpvulnerability_rest_apache_vulnerabilities() {
449 return wpvulnerability_rest_software_vulnerabilities( 'apache' );
450 }
451
452 /**
453 * Handle the Nginx vulnerabilities REST API request.
454 *
455 * @since 3.3.0
456 *
457 * @return WP_REST_Response Nginx vulnerabilities data or a message if none found.
458 */
459 function wpvulnerability_rest_nginx_vulnerabilities() {
460 return wpvulnerability_rest_software_vulnerabilities( 'nginx' );
461 }
462
463 /**
464 * Handle the MariaDB vulnerabilities REST API request.
465 *
466 * @since 3.4.0
467 *
468 * @return WP_REST_Response MariaDB vulnerabilities data or an empty array if none found.
469 */
470 function wpvulnerability_rest_mariadb_vulnerabilities() {
471 return wpvulnerability_rest_software_vulnerabilities( 'mariadb' );
472 }
473
474 /**
475 * Handle the MySQL vulnerabilities REST API request.
476 *
477 * @since 3.4.0
478 *
479 * @return WP_REST_Response MySQL vulnerabilities data or an empty array if none found.
480 */
481 function wpvulnerability_rest_mysql_vulnerabilities() {
482 return wpvulnerability_rest_software_vulnerabilities( 'mysql' );
483 }
484
485 /**
486 * Handle the ImageMagick vulnerabilities REST API request.
487 *
488 * @since 3.5.0
489 *
490 * @return WP_REST_Response ImageMagick vulnerabilities data or an empty array if none found.
491 */
492 function wpvulnerability_rest_imagemagick_vulnerabilities() {
493 return wpvulnerability_rest_software_vulnerabilities( 'imagemagick' );
494 }
495
496 /**
497 * Handle the curl vulnerabilities REST API request.
498 *
499 * @since 3.5.0
500 *
501 * @return WP_REST_Response curl vulnerabilities data or an empty array if none found.
502 */
503 function wpvulnerability_rest_curl_vulnerabilities() {
504 return wpvulnerability_rest_software_vulnerabilities( 'curl' );
505 }
506
507 /**
508 * Handle the memcached vulnerabilities REST API request.
509 *
510 * @since 3.5.0
511 *
512 * @return WP_REST_Response memcached vulnerabilities data or an empty array if none found.
513 */
514 function wpvulnerability_rest_memcached_vulnerabilities() {
515 return wpvulnerability_rest_software_vulnerabilities( 'memcached' );
516 }
517
518 /**
519 * Handle the Redis vulnerabilities REST API request.
520 *
521 * @since 3.5.0
522 *
523 * @return WP_REST_Response Redis vulnerabilities data or an empty array if none found.
524 */
525 function wpvulnerability_rest_redis_vulnerabilities() {
526 return wpvulnerability_rest_software_vulnerabilities( 'redis' );
527 }
528
529 /**
530 * Handle the SQLite vulnerabilities REST API request.
531 *
532 * @since 3.5.0
533 *
534 * @return WP_REST_Response SQLite vulnerabilities data or an empty array if none found.
535 */
536 function wpvulnerability_rest_sqlite_vulnerabilities() {
537 return wpvulnerability_rest_software_vulnerabilities( 'sqlite' );
538 }
539
540 /**
541 * Custom permission check for the WPVulnerability REST API.
542 *
543 * This function checks if the request is authenticated using an Application Password or
544 * an authenticated session and verifies the user has admin capabilities.
545 *
546 * @since 3.3.0
547 *
548 * @param WP_REST_Request $request The REST API request.
549 *
550 * @return bool True if the user has permission, false otherwise.
551 */
552 function wpvulnerability_permission_check( WP_REST_Request $request ) {
553
554 $capability = is_multisite() ? 'manage_network_options' : 'manage_options';
555
556 if ( is_user_logged_in() && current_user_can( $capability ) ) {
557 return true;
558 }
559
560 // Check if application passwords are available (WordPress 5.6+).
561 // On older WordPress versions this authentication method is skipped and
562 // only cookie-authenticated sessions with admin capabilities are accepted.
563 if ( function_exists( 'wp_is_application_passwords_available' ) && function_exists( 'wp_authenticate_application_password' ) && wp_is_application_passwords_available() ) {
564 $authorization_header = $request->get_header( 'authorization' );
565
566 // Check if the authorization header is present and properly formatted.
567 if ( $authorization_header && preg_match( '/^Basic\s(.+)$/i', $authorization_header, $matches ) ) {
568 $auth_string = base64_decode( (string) $matches[1], true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Decoding HTTP Basic auth; strict mode enforced.
569
570 if ( false === $auth_string ) {
571 return false;
572 }
573
574 $credentials = explode( ':', $auth_string, 2 );
575
576 if ( 2 !== count( $credentials ) ) {
577 return false;
578 }
579
580 $user = $credentials[0];
581 $password = $credentials[1];
582
583 // Authenticate the user using the application password.
584 $authenticated_user = wp_authenticate_application_password( null, $user, $password );
585
586 if ( $authenticated_user instanceof WP_User ) {
587 // Check if user has the required capability.
588 // For multisite, require manage_network_options.
589 // For single site, require manage_options.
590 if ( is_multisite() ) {
591 return user_can( $authenticated_user, 'manage_network_options' );
592 }
593
594 return user_can( $authenticated_user, 'manage_options' );
595 }
596 }
597 }
598
599 return false;
600 }
601
602 /**
603 * Registers REST API routes for WPVulnerability.
604 *
605 * This function sets up the REST API routes for WPVulnerability to handle requests
606 * related to vulnerabilities in various components like core, plugins, themes, PHP, and more.
607 *
608 * @since 3.3.0
609 *
610 * @return void
611 */
612 function wpvulnerability_register_rest_routes() {
613
614 // Define the endpoints to be registered.
615 $endpoints = array(
616 'core',
617 'plugins',
618 'themes',
619 'php',
620 'apache',
621 'nginx',
622 'mariadb',
623 'mysql',
624 'imagemagick',
625 'curl',
626 'memcached',
627 'redis',
628 'sqlite',
629 );
630
631 // Loop through each endpoint and register it.
632 foreach ( $endpoints as $endpoint ) {
633 register_rest_route(
634 'wpvulnerability/v1', // Namespace and version.
635 '/' . $endpoint, // Endpoint URL.
636 array(
637 'methods' => 'GET', // HTTP method.
638 'callback' => 'wpvulnerability_rest_' . $endpoint . '_vulnerabilities', // Callback function.
639 'permission_callback' => 'wpvulnerability_permission_check', // Permission check callback.
640 )
641 );
642 }
643 }
644
645 // Hook to initialize REST API endpoints.
646 add_action( 'rest_api_init', 'wpvulnerability_register_rest_routes' );
647