PluginProbe
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… / 3.0.5
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… v3.0.5
3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 2.11.12 2.11.11 2.11.10 2.11.9 2.11.7 2.11.8 2.11.6 2.11.5 2.11.4 2.11.3 2.11.1 2.11.2 2.11.0 2.10.5 2.10.4 2.10.3 2.10.2 2.10.1 2.10.0 All 93 releases
vigilante / includes / class-self-integrity-guidance.php

class-self-integrity-guidance.php in Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… 3.0.5, at includes/class-self-integrity-guidance.php

496 lines 28.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Guidance texts for self-protection (what a finding means, and how to fix it).
4 *
5 * Single source used by the File Integrity box, the Security Check detail, the
6 * Security Audit details, the admin notices and the alert email, so the five
7 * never drift apart. Strings and a mapping only: nothing here reads state or
8 * touches the filesystem.
9 *
10 * Several finding codes share one explanation, so the entry point is a case
11 * key (see case_key()), which is also what the File Integrity box groups by:
12 * ten modified files are one case with ten paths, not ten explanations.
13 *
14 * @package Vigilante
15 * @since 3.0.0
16 */
17
18 // Prevent direct access
19 if ( ! defined( 'ABSPATH' ) ) {
20 exit;
21 }
22
23 /**
24 * Class Vigilante_Self_Integrity_Guidance
25 */
26 class Vigilante_Self_Integrity_Guidance {
27
28 /**
29 * Case key for a finding. Codes with the same answer share a key.
30 *
31 * @param string $code Finding code.
32 * @param string $severity info|warning|critical.
33 * @param string $variant Optional variant set by the finding (unreadable,
34 * exec, link, dir, walk).
35 * @return string
36 */
37 public static function case_key( $code, $severity = 'critical', $variant = '' ) {
38 switch ( (string) $code ) {
39 case 'self_modified':
40 if ( 'unreadable' === $variant ) {
41 return 'unreadable';
42 }
43 return ( 'critical' === $severity ) ? 'modified_code' : 'modified_asset';
44 case 'self_missing':
45 return 'missing';
46 case 'self_symlink':
47 return 'link';
48 case 'self_extra':
49 if ( 'link' === $variant ) {
50 return 'link';
51 }
52 if ( 'dir' === $variant || 'walk' === $variant ) {
53 return 'unlistable';
54 }
55 return ( 'critical' === $severity ) ? 'extra_exec' : 'extra_file';
56 case 'manifest_replaced':
57 case 'manifest_stale':
58 return 'manifest_replaced';
59 case 'manifest_missing':
60 case 'manifest_invalid':
61 return ( 'critical' === $severity ) ? 'manifest_replaced' : 'manifest_soft';
62 case 'manifest_unverified':
63 return 'unverified';
64 case 'distribution_mismatch':
65 return 'distribution';
66 case 'self_downgraded':
67 return 'downgrade';
68 case 'no_anchors':
69 return 'no_anchors';
70 case 'cron_cleared_repeatedly':
71 // Critical only from the third time in a week or the fourth in
72 // 30 days (see Vigilante_Self_Integrity::watchdog_severity()).
73 return ( 'critical' === $severity ) ? 'cron_repeated' : 'cron_repeated_soft';
74 case 'self_disabled':
75 return 'disabled';
76 case 'self_hooks_removed':
77 return 'hooks_removed';
78 case 'self_stale':
79 return 'stale';
80 }
81 return 'unknown';
82 }
83
84 /**
85 * Guidance for one finding.
86 *
87 * @param array|string $finding Finding array, or a bare code.
88 * @return array { key, title, meaning, steps, repair }
89 */
90 public static function for_finding( $finding ) {
91 if ( is_array( $finding ) ) {
92 $code = isset( $finding['code'] ) ? (string) $finding['code'] : '';
93 $severity = isset( $finding['severity'] ) ? (string) $finding['severity'] : 'critical';
94 $variant = isset( $finding['variant'] ) ? (string) $finding['variant'] : '';
95 } else {
96 $code = (string) $finding;
97 $severity = 'critical';
98 $variant = '';
99 }
100 return self::case_data( self::case_key( $code, $severity, $variant ) );
101 }
102
103 /**
104 * Guidance for the state itself, when there is nothing wrong to explain
105 * (verified, verified with fewer references, turned off, never run).
106 *
107 * @param array $args {
108 * @type string $status Stored status (ok, degraded, warning, critical).
109 * @type int $files Files checked.
110 * @type array $anchors manifest/wporg/fingerprint booleans.
111 * @type bool $enabled Whether the self-check is on.
112 * @type bool $has_run Whether it has ever run.
113 * }
114 * @return array { key, title, meaning, steps, repair }
115 */
116 public static function for_status( $args = array() ) {
117 $status = isset( $args['status'] ) ? (string) $args['status'] : '';
118 $files = isset( $args['files'] ) ? (int) $args['files'] : 0;
119 $anchors = ( isset( $args['anchors'] ) && is_array( $args['anchors'] ) ) ? $args['anchors'] : array();
120 $enabled = ! isset( $args['enabled'] ) || (bool) $args['enabled'];
121 $has_run = ! isset( $args['has_run'] ) || (bool) $args['has_run'];
122
123 if ( ! $enabled ) {
124 return self::case_data( 'disabled' );
125 }
126 if ( ! $has_run ) {
127 return self::case_data( 'never_run' );
128 }
129 if ( $files < 1 ) {
130 return self::case_data( 'no_anchors' );
131 }
132
133 $present = count( array_filter( $anchors ) );
134 if ( $present >= 3 ) {
135 return self::case_data( 'verified' );
136 }
137 if ( empty( $anchors['wporg'] ) ) {
138 return self::case_data( 'verified_no_wporg' );
139 }
140 return self::case_data( 'verified_partial' );
141 }
142
143 /**
144 * Explanation for one Security Audit entry about self-protection: which
145 * files it was about, what it means and what to do, so the popup of the
146 * log says the same as the File Integrity box.
147 *
148 * @param string $action Event action.
149 * @param string|array $extra_data extra_data column (JSON string or array).
150 * @param string $severity Severity of the log entry.
151 * @return array|null { title, meaning, steps, files }
152 */
153 public static function for_log_event( $action, $extra_data = '', $severity = 'info' ) {
154 $cases = array(
155 'self_integrity_fail' => '',
156 'self_verified_post_update' => 'post_update',
157 'self_integrity_restored' => 'restored',
158 'self_integrity_scan' => 'checked',
159 'self_integrity_captured' => 'captured',
160 'self_rebaselined' => 'captured',
161 'cron_restored' => 'cron_restored',
162 'cron_scheduled' => 'cron_restored',
163 );
164 $action = (string) $action;
165 if ( ! isset( $cases[ $action ] ) ) {
166 return null;
167 }
168
169 $data = is_string( $extra_data ) ? json_decode( $extra_data, true ) : $extra_data;
170 $data = is_array( $data ) ? $data : array();
171
172 $files = array();
173 $codes = array();
174 if ( ! empty( $data['findings'] ) && is_array( $data['findings'] ) ) {
175 foreach ( $data['findings'] as $entry ) {
176 // The log stores each finding as "code:file".
177 $parts = explode( ':', (string) $entry, 2 );
178 $code = $parts[0];
179 $file = isset( $parts[1] ) ? $parts[1] : '';
180 $codes[] = $code;
181 if ( '' !== $file ) {
182 $files[] = $file;
183 }
184 }
185 }
186 foreach ( array( 'restored', 'scheduled' ) as $hook_key ) {
187 if ( ! empty( $data[ $hook_key ] ) && is_array( $data[ $hook_key ] ) ) {
188 foreach ( $data[ $hook_key ] as $hook ) {
189 $files[] = (string) $hook;
190 }
191 }
192 }
193
194 $key = $cases[ $action ];
195 if ( '' === $key ) {
196 $key = $codes ? self::case_key( $codes[0], 'critical' === $severity ? 'critical' : 'warning' ) : 'unknown';
197 }
198 if ( 'cron_restored' === $key && 'critical' === $severity ) {
199 $key = 'cron_repeated';
200 }
201
202 $guidance = self::case_data( $key );
203 $guidance['files'] = array_values( array_unique( $files ) );
204 if ( count( array_unique( $codes ) ) > 1 ) {
205 $guidance['meaning'] .= ' ' . __( 'This entry covers more than one kind of finding: File Integrity lists each one with what it means and what to do.', 'vigilante' );
206 }
207 return $guidance;
208 }
209
210 /**
211 * What to do when the repair button is not available.
212 *
213 * @param string $reason general|no_caps|network|renamed.
214 * @return array List of steps.
215 */
216 public static function manual_steps( $reason = 'general' ) {
217 if ( 'no_caps' === $reason ) {
218 return array(
219 __( 'Plugin files cannot be changed from WordPress on this site, so Vigilant cannot repair itself here. Ask whoever manages your server to reinstall Vigilant from WordPress.org.', 'vigilante' ),
220 );
221 }
222 if ( 'renamed' === $reason ) {
223 return array(
224 __( 'This copy of Vigilant lives in a folder with a different name, and the package WordPress.org distributes always installs as vigilante, so repairing from here would leave a clean copy beside the one that is running.', 'vigilante' ),
225 __( 'Download Vigilant from WordPress.org and replace the contents of the folder it is installed in, keeping the folder name, or rename the folder back to vigilante and repair from here.', 'vigilante' ),
226 );
227 }
228 if ( 'network' === $reason ) {
229 return array(
230 __( 'Only your network administrator can repair Vigilant, because its files are shared by every site in the network. Let them know what this page reports.', 'vigilante' ),
231 );
232 }
233 return array(
234 __( 'Download Vigilant from WordPress.org and install it from Plugins > Add New Plugin > Upload Plugin, choosing Replace current with uploaded. Your settings, tables and log are kept.', 'vigilante' ),
235 __( 'Do not delete Vigilant to reinstall it: deleting a plugin removes its settings, its tables and its log.', 'vigilante' ),
236 );
237 }
238
239 /**
240 * The three steps shared by every case that a reinstall fixes.
241 *
242 * @param bool $tail Whether to add the what-if-it-comes-back step.
243 * @return array
244 */
245 private static function repair_steps( $tail = true ) {
246 $steps = array(
247 __( 'Repair Vigilant: it downloads a clean copy from WordPress.org and replaces only the plugin files, so your settings, tables and log are kept.', 'vigilante' ),
248 __( 'Run a new scan from this tab to confirm the finding is gone.', 'vigilante' ),
249 );
250 if ( $tail ) {
251 $steps[] = __( 'If it comes back after repairing, someone can still write to your server: change your hosting, FTP and administrator passwords, review the administrator accounts and ask your host to check the server.', 'vigilante' );
252 }
253 return $steps;
254 }
255
256 /**
257 * Title, meaning and steps of each case.
258 *
259 * @param string $key Case key.
260 * @return array { key, title, meaning, steps, repair }
261 */
262 public static function case_data( $key ) {
263 $repair = true;
264 switch ( $key ) {
265 case 'modified_code':
266 $title = __( 'Code file modified', 'vigilante' );
267 $meaning = __( 'This Vigilant code file no longer matches the version published on WordPress.org. Something with write access to your server changed it: an attacker, a script, or a manual edit.', 'vigilante' );
268 $steps = self::repair_steps();
269 break;
270 case 'missing':
271 $title = __( 'File missing', 'vigilante' );
272 $meaning = __( 'A file that is part of Vigilant has been deleted. A missing module stops protecting your site without showing any error.', 'vigilante' );
273 $steps = self::repair_steps();
274 break;
275 case 'unreadable':
276 $title = __( 'File cannot be read', 'vigilante' );
277 $meaning = __( 'Vigilant cannot read this file, so it cannot check it. Files distributed by WordPress.org are always readable.', 'vigilante' );
278 $steps = array(
279 __( 'Repair Vigilant, which restores the file with standard permissions.', 'vigilante' ),
280 __( 'If repairing fails, ask your host to fix the permissions of wp-content/plugins/vigilante.', 'vigilante' ),
281 );
282 break;
283 case 'link':
284 $title = __( 'Symbolic link inside Vigilant', 'vigilante' );
285 $meaning = __( 'A Vigilant file is a symbolic link, or points outside the plugin folder. WordPress.org never distributes links, so someone put it there.', 'vigilante' );
286 $steps = self::repair_steps();
287 break;
288 case 'extra_exec':
289 $title = __( 'Executable file added', 'vigilante' );
290 $meaning = __( 'There is an executable file (PHP or similar) in the Vigilant folder that is not part of Vigilant. Hiding files inside a security plugin is a common way to keep a backdoor.', 'vigilante' );
291 $steps = array_merge(
292 array( __( 'If you want to know how it got there, ask your host to check when the file was created before you repair, because repairing removes it.', 'vigilante' ) ),
293 self::repair_steps()
294 );
295 break;
296 case 'unlistable':
297 $title = __( 'Part of the folder cannot be checked', 'vigilante' );
298 $meaning = __( 'Vigilant cannot list one of its folders, or found far more files than it ships, so part of its own folder cannot be checked. A web server can still run files hidden in a folder that cannot be listed.', 'vigilante' );
299 $steps = array(
300 __( 'Repair Vigilant, which replaces the folder with a clean copy.', 'vigilante' ),
301 __( 'If repairing fails, ask your host to check the permissions of wp-content/plugins/vigilante.', 'vigilante' ),
302 );
303 break;
304 case 'extra_file':
305 $title = __( 'Extra file that is not code', 'vigilante' );
306 $meaning = __( 'There is a file in the Vigilant folder that is not part of Vigilant, and it is not executable code. It is often a PHP error log written by your server, or something left behind by a backup or sync tool.', 'vigilante' );
307 $steps = array(
308 __( 'If it is an error_log, your server is writing PHP errors inside Vigilant folder: open it, and if the errors mention Vigilant, report them in the Vigilant support forum.', 'vigilante' ),
309 __( 'Repairing Vigilant removes the file, because the folder is replaced with a clean copy.', 'vigilante' ),
310 );
311 break;
312 case 'manifest_replaced':
313 $title = __( 'Manifest replaced, deleted or damaged', 'vigilante' );
314 $meaning = __( 'MANIFEST.sha256, the list of fingerprints Vigilant uses to check its own files, was replaced, deleted or damaged without a plugin update. Replacing that list is how someone would hide changes to the plugin.', 'vigilante' );
315 $steps = self::repair_steps();
316 break;
317 case 'manifest_soft':
318 $title = __( 'Manifest missing or damaged', 'vigilante' );
319 $meaning = __( 'MANIFEST.sha256 is missing or is not a valid manifest. Vigilant still checks its files against WordPress.org, but one of its three references is gone.', 'vigilante' );
320 $steps = array(
321 __( 'Repair Vigilant to restore the manifest.', 'vigilante' ),
322 );
323 break;
324 case 'distribution':
325 $title = __( 'Different from the WordPress.org copy', 'vigilante' );
326 $meaning = __( 'These files match the manifest inside the plugin, but not the copy WordPress.org distributes for this version. On a development copy that is expected; on a live site it means the whole plugin, manifest included, came from somewhere else.', 'vigilante' );
327 $steps = array(
328 __( 'If you installed Vigilant from WordPress.org, repair it.', 'vigilante' ),
329 __( 'If you are running a development build on purpose, install the published version when you finish testing.', 'vigilante' ),
330 );
331 break;
332 case 'modified_asset':
333 $title = __( 'Stylesheet, script or image modified', 'vigilante' );
334 $meaning = __( 'A stylesheet, script or image of Vigilant no longer matches the distributed version. Usually an optimization or cache plugin, or your host, rewrote it while minifying or combining files.', 'vigilante' );
335 $steps = array(
336 __( 'If you use an optimization or cache plugin, exclude wp-content/plugins/vigilante from it, then repair Vigilant.', 'vigilante' ),
337 __( 'If you do not use one, repair Vigilant and run a new scan.', 'vigilante' ),
338 );
339 break;
340 case 'downgrade':
341 $title = __( 'Older version installed', 'vigilante' );
342 $meaning = __( 'Vigilant was replaced by an older version. Older versions can contain vulnerabilities that are already fixed and public.', 'vigilante' );
343 $steps = array(
344 __( 'If you did not downgrade it on purpose, repair Vigilant: it installs the version WordPress.org distributes now.', 'vigilante' ),
345 );
346 break;
347 case 'unverified':
348 $title = __( 'Version changed, not confirmed yet', 'vigilante' );
349 $meaning = __( 'The Vigilant version changed without the WordPress updater (an FTP upload, a file manager, a deployment), and the new files cannot be confirmed yet because WordPress.org has not published checksums for that version.', 'vigilante' );
350 $steps = array(
351 __( 'If you just updated Vigilant by hand with a copy from WordPress.org, you do not need to do anything: the next checks confirm it once WordPress.org publishes the checksums, usually within a day.', 'vigilante' ),
352 __( 'If you did not update it, or the copy did not come from WordPress.org, repair Vigilant.', 'vigilante' ),
353 );
354 break;
355 case 'no_anchors':
356 $title = __( 'Vigilant could not check its files', 'vigilante' );
357 $meaning = __( 'The manifest is not available and WordPress.org could not be reached, so there was nothing to check the files against.', 'vigilante' );
358 $steps = array(
359 __( 'Open Tools > Site Health. If your server cannot connect to WordPress.org, ask your host to allow it.', 'vigilante' ),
360 __( 'Then repair Vigilant to restore the manifest.', 'vigilante' ),
361 );
362 break;
363 /*
364 * Two cases since 3.0.5, and neither says that anything is switched
365 * off: by the time somebody reads this the watchdog has scheduled
366 * the tasks again. The first cause named is the one that needs no
367 * culprit, WordPress losing the task; "a possible compromise" is
368 * the last step, and only of the case that earns it.
369 */
370 case 'cron_repeated_soft':
371 $title = __( 'Scheduled tasks were missing more than once', 'vigilante' );
372 $meaning = __( 'Vigilant found some of its scheduled tasks missing more than once this month and scheduled them again each time. This is about scheduled tasks, not about changed files. WordPress drops a scheduled task by itself when it cannot save its next run, for example when the database does not answer at that moment, and a cleanup plugin can remove them too.', 'vigilante' );
373 $steps = array(
374 __( 'There is nothing to repair: Vigilant schedules a missing task again whenever it finds one. A plugin such as WP Crontrol lists the scheduled events of your site, and the ones of Vigilant start with vigilante_.', 'vigilante' ),
375 __( 'If you use a cleanup or optimization plugin that removes scheduled events, exclude the ones whose name starts with vigilante_.', 'vigilante' ),
376 __( 'This notice goes away by itself after a week with no task missing.', 'vigilante' ),
377 );
378 $repair = false;
379 break;
380 case 'cron_repeated':
381 $title = __( 'Scheduled tasks keep disappearing', 'vigilante' );
382 $meaning = __( 'Vigilant scheduled tasks keep going missing, and Vigilant scheduled them again each time. This is about scheduled tasks, not about changed files. That often is not WordPress losing a task: something on this site keeps removing them, and between one removal and the next check the scheduled scans and the daily check do not run.', 'vigilante' );
383 $steps = array(
384 __( 'Look for a cleanup or optimization plugin, or a scheduled job of your hosting, that removes scheduled events, and exclude the ones whose name starts with vigilante_. A plugin such as WP Crontrol lists them.', 'vigilante' ),
385 __( 'If you find neither, treat it as a possible compromise: review recently installed plugins and the administrator accounts.', 'vigilante' ),
386 __( 'This notice goes away by itself after a week with no task missing.', 'vigilante' ),
387 );
388 $repair = false;
389 break;
390 case 'cron_restored':
391 $title = __( 'Scheduled task restored', 'vigilante' );
392 $meaning = __( 'One of Vigilant scheduled tasks had disappeared and Vigilant scheduled it again. A cron cleanup plugin, a database restore or a site migration can do that.', 'vigilante' );
393 $steps = array(
394 __( 'Nothing to do if it does not happen again. A plugin such as WP Crontrol lists the scheduled events of your site.', 'vigilante' ),
395 );
396 $repair = false;
397 break;
398 case 'restored':
399 $title = __( 'Integrity restored', 'vigilante' );
400 $meaning = __( 'The files that had been reported match what WordPress.org distributes again, so what was reported before is closed.', 'vigilante' );
401 $steps = array();
402 $repair = false;
403 break;
404 case 'post_update':
405 $title = __( 'Checked right after updating', 'vigilante' );
406 $meaning = __( 'WordPress updated Vigilant and Vigilant checked its own files at the end of that same request, which is when a half copied or replaced file would show up.', 'vigilante' );
407 $steps = array();
408 $repair = false;
409 break;
410 case 'checked':
411 $title = __( 'Files checked', 'vigilante' );
412 $meaning = __( 'Vigilant checked its own files against the references available and found nothing to report.', 'vigilante' );
413 $steps = array();
414 $repair = false;
415 break;
416 case 'captured':
417 $title = __( 'Reference saved', 'vigilante' );
418 $meaning = __( 'Vigilant saved the fingerprint of its manifest in your database. That fingerprint is what makes a swapped or deleted manifest detectable later.', 'vigilante' );
419 $steps = array();
420 $repair = false;
421 break;
422 case 'verified':
423 $title = __( 'Verified', 'vigilante' );
424 $meaning = __( 'Vigilant checked its own files against its three references and they all match: the checksums WordPress.org publishes for this version, the manifest shipped inside the plugin, and the fingerprint kept in your database.', 'vigilante' );
425 $steps = array();
426 $repair = false;
427 break;
428 case 'verified_no_wporg':
429 $title = __( 'Verified with fewer references', 'vigilante' );
430 $meaning = __( 'The files match the manifest shipped inside the plugin and the fingerprint kept in your database, but WordPress.org has not published checksums for this version yet, which is normal for a few hours after a release, or your server could not reach it.', 'vigilante' );
431 $steps = array(
432 __( 'Nothing to do. If this lasts more than two days, open Tools > Site Health and check that your server can connect to WordPress.org.', 'vigilante' ),
433 );
434 $repair = false;
435 break;
436 case 'verified_partial':
437 $title = __( 'Verified with fewer references', 'vigilante' );
438 $meaning = __( 'The files were checked, but one of the three references was not available for this check.', 'vigilante' );
439 $steps = array(
440 __( 'Nothing to do. The next check uses every reference that is available then.', 'vigilante' ),
441 );
442 $repair = false;
443 break;
444 case 'disabled':
445 $title = __( 'Self-protection is switched off by code', 'vigilante' );
446 $meaning = __( 'Nothing is checking that Vigilant own files are intact, so a change to them would go unnoticed. There is no setting that does this: something on this site is using the vigilante_self_integrity_enabled filter, and the files listed here are the ones that hook it.', 'vigilante' );
447 $steps = array(
448 __( 'If you did not ask for this, treat it as a compromise: a plugin or a snippet that switches off the check of a security plugin is doing the one thing an attacker needs first.', 'vigilante' ),
449 __( 'Open the files listed above and remove that filter, or deactivate whatever added it. The check starts again on its own.', 'vigilante' ),
450 __( 'If you turned it off on purpose because this site must not contact WordPress.org, nothing else is wrong: the plugin says so on every screen while it lasts.', 'vigilante' ),
451 );
452 $repair = false;
453 break;
454 case 'hooks_removed':
455 $title = __( 'Something removed the self-protection hooks', 'vigilante' );
456 $meaning = __( 'Code on this site unhooked checks that Vigilant runs on its own files and on its scheduled tasks, so Vigilant would keep showing its last result as if it were current. WordPress says nothing when a callback is removed, so the file that did it cannot be named; the plugins loaded in that request are in the entry of the Security Audit.', 'vigilante' );
457 $steps = array(
458 __( 'Look at the Security Audit entry for the list of plugins loaded when it happened, and deactivate them one by one until it stops.', 'vigilante' ),
459 __( 'If none of them explains it, treat it as a compromise: change your hosting, FTP and administrator passwords and ask your host to check the server.', 'vigilante' ),
460 );
461 $repair = false;
462 break;
463 case 'stale':
464 $title = __( 'Not checked recently', 'vigilante' );
465 $meaning = __( 'The last check is more than three days old, so what you see is not a current result. It happens on sites nobody opens and with no cron running, and it also happens when something stopped the check.', 'vigilante' );
466 $steps = array(
467 __( 'Run a new scan from this tab. If the date does not move, your scheduled tasks are not running: check them with a plugin such as WP Crontrol.', 'vigilante' ),
468 );
469 $repair = false;
470 break;
471 case 'never_run':
472 $title = __( 'Not checked yet', 'vigilante' );
473 $meaning = __( 'Vigilant has not checked its own files yet. It checks them in every integrity scan, right after every update, and once a day.', 'vigilante' );
474 $steps = array(
475 __( 'Run a scan from this tab to check them now.', 'vigilante' ),
476 );
477 $repair = false;
478 break;
479 default:
480 $key = 'unknown';
481 $title = __( 'Finding in Vigilant own files', 'vigilante' );
482 $meaning = __( 'Vigilant reported something about its own files that this version has no explanation for.', 'vigilante' );
483 $steps = self::repair_steps();
484 break;
485 }
486
487 return array(
488 'key' => $key,
489 'title' => $title,
490 'meaning' => $meaning,
491 'steps' => $steps,
492 'repair' => $repair,
493 );
494 }
495 }
496