PluginProbe
WPVulnerability / 5.1.2
WPVulnerability v5.1.2
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-software.php

wpvulnerability-software.php in WPVulnerability 5.1.2, at wpvulnerability-software.php

278 lines 8.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Software functions
4 *
5 * @package WPVulnerability
6 *
7 * @version 3.5.0
8 */
9
10 defined( 'ABSPATH' ) || die( 'No script kiddies please!' );
11
12 /**
13 * Retrieves the specified software version.
14 *
15 * This function returns the version of PHP, Apache, Nginx, MySQL, or MariaDB
16 * after performing necessary validations. It ensures that the returned value
17 * is clean and sanitized.
18 *
19 * @since 3.5.0
20 * @since 4.3.0 Updated to handle hybrid detection format from ImageMagick, Redis, Memcached, and SQLite.
21 *
22 * @param string $software The name of the software ('php', 'apache', 'nginx', 'mysql', 'mariadb').
23 * @return string|null The sanitized version of the software, or null if not found.
24 */
25 function wpvulnerability_get_software_version( $software ) {
26 switch ( $software ) {
27 case 'php':
28 $php_version = wpvulnerability_detect_php();
29 if ( null !== $php_version && '' !== $php_version ) {
30 return wp_kses( (string) $php_version, 'strip' );
31 }
32
33 break;
34
35 case 'apache':
36 case 'nginx':
37 $webserver = wpvulnerability_detect_webserver();
38 if ( isset( $webserver['id'] ) && $webserver['id'] === $software && ! empty( $webserver['version'] ) ) {
39 return wp_kses( (string) $webserver['version'], 'strip' );
40 }
41 break;
42
43 case 'mysql':
44 case 'mariadb':
45 $sqlserver = wpvulnerability_detect_sqlserver();
46 if ( isset( $sqlserver['id'] ) && $sqlserver['id'] === $software && ! empty( $sqlserver['version'] ) ) {
47 return wp_kses( (string) $sqlserver['version'], 'strip' );
48 }
49 break;
50
51 case 'imagemagick':
52 $detection = wpvulnerability_detect_imagemagick();
53 if ( isset( $detection['version'] ) && 'unknown' !== $detection['version'] ) {
54 return wp_kses( (string) $detection['version'], 'strip' );
55 }
56
57 break;
58
59 case 'curl':
60 $curl_version = wpvulnerability_detect_curl();
61 if ( null !== $curl_version && '' !== $curl_version ) {
62 return wp_kses( (string) $curl_version, 'strip' );
63 }
64
65 break;
66
67 case 'memcached':
68 $detection = wpvulnerability_detect_memcached();
69 if ( isset( $detection['version'] ) && 'unknown' !== $detection['version'] ) {
70 return wp_kses( (string) $detection['version'], 'strip' );
71 }
72
73 break;
74
75 case 'redis':
76 $detection = wpvulnerability_detect_redis();
77 if ( isset( $detection['version'] ) && 'unknown' !== $detection['version'] ) {
78 return wp_kses( (string) $detection['version'], 'strip' );
79 }
80
81 break;
82
83 case 'sqlite':
84 $detection = wpvulnerability_detect_sqlite();
85 if ( isset( $detection['version'] ) && 'unknown' !== $detection['version'] ) {
86 return wp_kses( (string) $detection['version'], 'strip' );
87 }
88
89 break;
90
91 default:
92 break;
93 }
94
95 return null;
96 }
97
98 /**
99 * Retrieves vulnerabilities for a given software version and updates its data.
100 *
101 * This function detects the installed software version, checks for vulnerabilities using an external API,
102 * and updates the data array with the vulnerabilities found.
103 *
104 * @since 3.5.0
105 *
106 * @param string $software The software name (e.g., 'php', 'apache', 'nginx', 'mysql', 'mariadb').
107 *
108 * @return array<string, mixed> The updated data array containing vulnerability information.
109 */
110 function wpvulnerability_get_fresh_vulnerabilities( $software ) {
111
112 $version = null;
113 $data = array(
114 'vulnerabilities' => null,
115 'vulnerable' => 0,
116 'lifecycle' => array(),
117 );
118
119 switch ( $software ) {
120 case 'php':
121 case 'apache':
122 case 'nginx':
123 case 'mysql':
124 case 'mariadb':
125 case 'imagemagick':
126 case 'curl':
127 case 'memcached':
128 case 'redis':
129 case 'sqlite':
130 $version = wpvulnerability_get_software_version( $software );
131 break;
132
133 default:
134 return $data;
135 }
136
137 if ( $version ) {
138 $transient_key = 'wpvulnerability_' . $software;
139
140 // Delete the transient so the next call fetches fresh data and repopulates it.
141 if ( is_multisite() ) {
142 delete_site_transient( $transient_key );
143 } else {
144 delete_transient( $transient_key );
145 }
146
147 switch ( $software ) {
148 case 'php':
149 case 'apache':
150 case 'nginx':
151 case 'mysql':
152 case 'mariadb':
153 case 'imagemagick':
154 case 'curl':
155 case 'memcached':
156 case 'redis':
157 case 'sqlite':
158 // cache=1: transient was just cleared, so a fresh API call is made and result cached.
159 $api_response = wpvulnerability_get_vulnerabilities( $software, $version, 1 );
160 break;
161 }
162
163 if ( ! empty( $api_response ) ) {
164 $data['vulnerabilities'] = $api_response;
165 $data['vulnerable'] = 1;
166 }
167
168 // Read back the transient to extract lifecycle fields from the full API response.
169 $raw_body = is_multisite() ? get_site_transient( $transient_key ) : get_transient( $transient_key );
170 $raw_response = json_decode( is_string( $raw_body ) ? $raw_body : '', true );
171 if ( is_array( $raw_response ) && isset( $raw_response['data'] ) && is_array( $raw_response['data'] ) ) {
172 $resp_data = $raw_response['data'];
173 $data['lifecycle'] = array(
174 'name' => is_scalar( $resp_data['name'] ?? '' ) ? (string) ( $resp_data['name'] ?? '' ) : '',
175 'status' => is_scalar( $resp_data['status'] ?? '' ) ? (string) ( $resp_data['status'] ?? '' ) : '',
176 'date_start' => is_scalar( $resp_data['date_start'] ?? '' ) ? (string) ( $resp_data['date_start'] ?? '' ) : '',
177 'date_end' => is_scalar( $resp_data['date_end'] ?? '' ) ? (string) ( $resp_data['date_end'] ?? '' ) : '',
178 );
179 }
180 }
181
182 return $data;
183 }
184
185
186 /**
187 * Get Installed Software
188 *
189 * Retrieves the list of installed software versions, checks for vulnerabilities,
190 * caches the data, and sends an email notification if vulnerabilities are detected.
191 *
192 * @since 3.5.0
193 *
194 * @param string $software The software name (e.g., 'php', 'apache').
195 *
196 * @return string JSON-encoded array of software data with vulnerabilities and vulnerable status.
197 */
198 function wpvulnerability_get_installed( $software ) {
199
200 $wpvulnerability_software_vulnerable = 0;
201
202 // Retrieve fresh vulnerabilities for the installed software version.
203 $data = wpvulnerability_get_fresh_vulnerabilities( $software );
204
205 // Check if the software version is vulnerable and count the vulnerabilities.
206 if ( isset( $data['vulnerable'] ) && is_numeric( $data['vulnerable'] ) && (int) $data['vulnerable'] ) {
207 $vulns = isset( $data['vulnerabilities'] ) && is_array( $data['vulnerabilities'] ) ? $data['vulnerabilities'] : array();
208 $wpvulnerability_software_vulnerable = count( $vulns );
209 }
210
211 // Cache the vulnerability data and the timestamp for cache expiration.
212 if ( is_multisite() ) {
213 update_site_option( 'wpvulnerability-' . $software, wp_json_encode( $data ) );
214 update_site_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ) );
215 update_site_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
216 } else {
217 update_option( 'wpvulnerability-' . $software, wp_json_encode( $data ), false );
218 update_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ), false );
219 update_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ), false );
220 }
221
222 // Return the JSON-encoded array of software data.
223 $encoded = wp_json_encode( $data );
224 return false !== $encoded ? $encoded : '';
225 }
226
227 /**
228 * Get cached software vulnerabilities without triggering remote calls.
229 *
230 * @since 3.5.0
231 *
232 * @param string $software The software name (e.g., 'php', 'apache').
233 *
234 * @return array<string, mixed>|null Array of software data with vulnerabilities, or null if software is invalid.
235 */
236 function wpvulnerability_software_get_vulnerabilities( $software ) {
237
238 $valid_software = array( 'php', 'apache', 'mariadb', 'mysql', 'nginx', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' );
239
240 // Use strict comparison for in_array.
241 if ( in_array( $software, $valid_software, true ) ) {
242 if ( is_multisite() ) {
243 $raw = get_site_option( 'wpvulnerability-' . $software );
244 $data = json_decode( is_string( $raw ) ? $raw : '', true );
245 } else {
246 $raw = get_option( 'wpvulnerability-' . $software );
247 $data = json_decode( is_string( $raw ) ? $raw : '', true );
248 }
249
250 return is_array( $data ) ? $data : array();
251 } else {
252 return null;
253 }
254 }
255
256 /**
257 * Update the software cache and remove any old cache data.
258 *
259 * @since 3.0.0
260 *
261 * @param string $software The software name (e.g., 'php', 'apache').
262 *
263 * @return void
264 */
265 function wpvulnerability_get_vulnerabilities_clean( $software ) {
266
267 // Skip detection for components the administrator has hidden (either via the
268 // analysis settings or a WPVULNERABILITY_HIDE_* wp-config constant). This
269 // prevents shell_exec from running for components that are meant to be
270 // deactivated, matching the behaviour of the core/plugins/themes clean functions.
271 if ( ! wpvulnerability_analyze_filter( $software ) ) {
272 return;
273 }
274
275 // Update the installed software cache.
276 wpvulnerability_get_installed( $software );
277 }
278