$id ) : array() ) . ( $id ? '#avai-finding-review' : '' ); } /** Display labels only; stored statuses and exported evidence keep their original values. */ public static function statusLabel( mixed $status ): string { $labels = array( 'open' => __( 'Open', 'shieldlume-security' ), 'acknowledged' => __( 'Reviewed', 'shieldlume-security' ), 'excepted' => __( 'Accepted exception', 'shieldlume-security' ), 'resolved' => __( 'Resolved', 'shieldlume-security' ), ); return is_string( $status ) && isset( $labels[ $status ] ) ? $labels[ $status ] : __( 'Unknown status', 'shieldlume-security' ); } /** Only stored scalar text is rendered; arrays/objects are never coerced into claims. */ public static function text( mixed $value, string $fallback = '', int $limit = 4000 ): string { if ( ! is_string( $value ) || '' === trim( $value ) ) { return $fallback; } return strlen( $value ) > $limit ? substr( $value, 0, $limit ) . '…' : $value; } /** Priority is a sorting ordinal, never a probability or a new detector assessment. */ public static function describe( array $finding ): array { $detector = self::text( $finding['detector_id'] ?? null ); $severity = self::text( $finding['severity'] ?? null ); $status = self::text( $finding['_status'] ?? null ); $guide = array( 'title' => __( 'An observation needs review', 'shieldlume-security' ), 'kind' => __( 'Recorded observation', 'shieldlume-security' ), 'explanation' => __( 'This finding needs a closer look. Review the saved observation, its source and any limits before deciding what it means.', 'shieldlume-security' ), 'why' => __( 'An unfamiliar detector or incomplete record needs verification; a label alone does not establish a compromise.', 'shieldlume-security' ), 'next' => __( 'Open the full evidence and confirm the observation with the site owner, host or component maintainer. Do not change or remove content solely on this summary.', 'shieldlume-security' ), 'priority' => array( 'critical' => 5, 'high' => 4, 'medium' => 3, 'low' => 2, 'info' => 1 )[ $severity ] ?? 1, 'active' => in_array( $status, array( 'open', 'acknowledged' ), true ), ); $evidence = is_array( $finding['evidence'] ?? null ) ? $finding['evidence'] : array(); $first = is_array( $evidence[0] ?? null ) ? $evidence[0] : array(); $eligibility = is_array( $finding['action_eligibility'] ?? null ) ? $finding['action_eligibility'] : array(); if ( in_array( $detector, array( 'integrity.core', 'integrity.core.missing', 'integrity.repository' ), true ) ) { $missing = 'integrity.core.missing' === $detector || in_array( $first['type'] ?? null, array( 'missing_reference_file', 'missing_repository_file' ), true ); $guide['kind'] = __( 'File integrity', 'shieldlume-security' ); $guide['title'] = $missing ? __( 'An expected file is missing', 'shieldlume-security' ) : __( 'A file differs from its reference copy', 'shieldlume-security' ); $guide['explanation'] = $missing ? __( 'A file listed in the recorded reference was absent. A deployment choice or a damaged installation can explain this. A missing file alone does not establish malware.', 'shieldlume-security' ) : __( 'At the recorded check, this file differed from the reference copy for its installed version. An intentional edit or damage can explain the change; the difference alone does not establish malware.', 'shieldlume-security' ); $guide['why'] = __( 'Unexpected changes can affect security or break the site. Restoring the same version does not fix a vulnerability in that version.', 'shieldlume-security' ); $guide['next'] = $missing ? __( 'Confirm whether the absence is intentional. Preserve recovery material, then use the official update or reinstall process if restoration is needed. This beta does not create missing files.', 'shieldlume-security' ) : ( true === ( $eligibility['repair'] ?? null ) ? __( 'Review the difference and confirm any customization. Use a trusted backup or the official WordPress reinstall process if replacement is needed. If Pro is installed, eligible files also offer a separately approved repair preview with protected recovery and verification.', 'shieldlume-security' ) : __( 'Review the difference with the component maintainer. No eligible replacement is recorded here; preserve recovery material before a separately reviewed update or reinstall.', 'shieldlume-security' ) ); if ( 'unexpected_repository_file' === ( $first['type'] ?? null ) ) { $guide['title'] = __( 'An extra file needs checking', 'shieldlume-security' ); $guide['explanation'] = __( 'The observed file is not listed in the recorded repository manifest. A generated file or a local customization can explain this; it is not a checksum mismatch and does not by itself establish malware.', 'shieldlume-security' ); $guide['next'] = __( 'Confirm the file’s origin and purpose with the component maintainer. Preserve evidence before taking action. This beta does not automatically remove unexpected files.', 'shieldlume-security' ); } } elseif ( 1 === preg_match( '/\Avulnerability\.wordfence\.[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}\z/', $detector ) ) { $guide['kind'] = __( 'Software advisory', 'shieldlume-security' ); $guide['title'] = __( 'An installed version has a reported vulnerability', 'shieldlume-security' ); $guide['explanation'] = __( 'The recorded version is listed as affected in a software advisory. Review the recommended update or workaround. This does not show that the site was attacked or that the issue can be exploited here.', 'shieldlume-security' ); $guide['why'] = __( 'A known weakness may need a software update or mitigation. A same-version file repair does not patch that weakness.', 'shieldlume-security' ); $guide['next'] = false === ( $first['patched'] ?? null ) ? __( 'No patched release is listed in this record. Check the current advisory and vendor guidance, then assess whether to disable or replace the component. Consider the effect on site functionality.', 'shieldlume-security' ) : __( 'Open the advisory and check its freshness, affected range and vendor guidance. Preserve a backup before updating to a confirmed patched release, then scan again.', 'shieldlume-security' ); } elseif ( 'signature.exact-sha256' === $detector ) { $guide['kind'] = __( 'Signed indicator match', 'shieldlume-security' ); $guide['title'] = __( 'Content matches a signed indicator', 'shieldlume-security' ); $guide['explanation'] = __( 'The file’s digital fingerprint matches an indicator in the trusted signed rules. Review its publisher and evidence; a match alone does not explain how an attack happened.', 'shieldlume-security' ); $guide['why'] = __( 'A matching indicator deserves prompt review. It does not identify every affected file or establish how a compromise occurred.', 'shieldlume-security' ); $guide['next'] = __( 'Preserve the evidence, inspect the indicator source and bundle freshness, and assess related findings. Use an eligible trusted repair preview only after recovery checks; do not automatically delete the file.', 'shieldlume-security' ); } elseif ( 'engine.clamav' === $detector ) { $guide['kind'] = __( 'Local engine match', 'shieldlume-security' ); $guide['title'] = __( 'The local engine reported a match', 'shieldlume-security' ); $guide['explanation'] = __( 'The host-configured ClamAV engine returned a signature or heuristic match. Its rules may include host-added rules. The result needs review and does not by itself establish malicious intent.', 'shieldlume-security' ); $guide['why'] = __( 'The named signature and database version help the host check whether this is a harmful file or a false positive.', 'shieldlume-security' ); $guide['next'] = __( 'Ask the host to review the recorded signature and rule freshness. Compare with a trusted copy before choosing a repair; do not automatically delete the file.', 'shieldlume-security' ); } elseif ( 'database.default-admin-role' === $detector ) { $guide['kind'] = __( 'Account configuration', 'shieldlume-security' ); $guide['title'] = __( 'New users may receive administrator access', 'shieldlume-security' ); $guide['explanation'] = __( 'The recorded default role is administrator. If public registration is enabled, a new registrant may receive administrative access. This is a configuration exposure, not proof of an infection.', 'shieldlume-security' ); $guide['why'] = __( 'An administrator can change site content, settings and other accounts. Check registration settings alongside this role.', 'shieldlume-security' ); $guide['next'] = __( 'Review WordPress registration and default-role settings with the owner. If unintended, choose an appropriate lower-privilege role and review recent users. This guide changes no settings.', 'shieldlume-security' ); } elseif ( 'database.cron-anomaly' === $detector ) { $guide['kind'] = __( 'Scheduled task', 'shieldlume-security' ); $guide['title'] = __( 'A scheduled task needs a closer look', 'shieldlume-security' ); $guide['explanation'] = __( 'The saved schedule contains an unusually frequent task or a malformed entry. A legitimate plugin can intentionally run frequent work; its hook name does not prove malicious behavior.', 'shieldlume-security' ); $guide['why'] = __( 'Unexpected schedules can consume resources or indicate a broken configuration. Their purpose needs confirmation before removal.', 'shieldlume-security' ); $guide['next'] = __( 'Read the recorded task details and identify the responsible plugin with its maintainer. Confirm the intended interval before changing a schedule.', 'shieldlume-security' ); } elseif ( in_array( $detector, array( 'inventory.new-administrator', 'inventory.administrator-removed' ), true ) ) { $added = 'inventory.new-administrator' === $detector; $guide['kind'] = __( 'Administrator change', 'shieldlume-security' ); $guide['title'] = $added ? __( 'New administrator access was observed', 'shieldlume-security' ) : __( 'Administrator access was no longer observed', 'shieldlume-security' ); $guide['explanation'] = __( 'The administrator inventory changed between observations. A legitimate account or role change can explain this; the comparison does not identify who made it or prove an account takeover.', 'shieldlume-security' ); $guide['why'] = __( 'Administrator access controls the site. An unexpected change should be checked promptly with the owner.', 'shieldlume-security' ); $guide['next'] = __( 'Confirm the account or role change with the owner and review WordPress user management. If unexpected, investigate access and recent activity before making a recovery decision.', 'shieldlume-security' ); } elseif ( 'inventory.persistence-change' === $detector ) { $guide['kind'] = __( 'Background component change', 'shieldlume-security' ); $guide['title'] = __( 'Something running behind the scenes changed', 'shieldlume-security' ); $guide['explanation'] = __( 'A must-use plugin, WordPress drop-in or recurring task changed between two complete inventory observations. Ordinary installation, updating or removal can explain this. Neither inventory is a trusted-clean baseline.', 'shieldlume-security' ); $guide['why'] = __( 'These components can run behind the scenes. Unexpected changes deserve review, but names and metadata do not establish malicious behavior.', 'shieldlume-security' ); $guide['next'] = __( 'Compare the recorded before and after values with recent maintenance. Confirm the change with the owner or component maintainer and review related file findings.', 'shieldlume-security' ); } else { $heuristics = self::heuristics(); if ( isset( $heuristics[ $detector ] ) ) { $guide['kind'] = __( 'Structural observation', 'shieldlume-security' ); $guide['title'] = $heuristics[ $detector ][0]; $guide['explanation'] = $heuristics[ $detector ][1] . ' ' . __( 'This code pattern can also appear in legitimate software. The check does not prove that it runs or is malicious.', 'shieldlume-security' ); $guide['why'] = __( 'If this behavior is reachable with unsafe input, it may let untrusted data influence site behavior. Input validation and intended use need human review.', 'shieldlume-security' ); $guide['next'] = __( 'Open the recorded observation and ask the component maintainer to review its input handling and intended behavior. Preserve recovery material; do not remove custom code solely because a pattern matched.', 'shieldlume-security' ); $scope = is_array( $finding['scope'] ?? null ) ? $finding['scope'] : array(); if ( 'database' === ( $scope['type'] ?? null ) ) { $guide['explanation'] .= ' ' . __( 'This observation came from a selected database value, not a file.', 'shieldlume-security' ); } } } if ( 'resolved' === $status ) { $guide['next'] = __( 'This record is resolved. Read the retained resolution and verification evidence before drawing conclusions; a resolved finding does not prove the whole site is clean.', 'shieldlume-security' ); $guide['priority'] = 0; } elseif ( 'excepted' === $status ) { $guide['next'] = __( 'A scoped exception is recorded. Review its reason and exact scope in the full record. Keep the evidence; meaningful content or rule changes cause reconsideration.', 'shieldlume-security' ); $guide['priority'] = 0; } return $guide; } private static function heuristics(): array { return array( 'php.request-execution' => array( __( 'Request data reaches an execution call', 'shieldlume-security' ), __( 'A PHP code or command execution expression directly contains request-controlled data.', 'shieldlume-security' ) ), 'php.assigned-request-execution' => array( __( 'A request-derived variable reaches execution', 'shieldlume-security' ), __( 'A variable assigned request data in the preceding statement is passed to a PHP execution call.', 'shieldlume-security' ) ), 'php.decoded-execution' => array( __( 'Decoded content reaches dynamic execution', 'shieldlume-security' ), __( 'A PHP execution expression contains a decoder. Encoding can serve legitimate purposes and is not itself malware.', 'shieldlume-security' ) ), 'php.request-include' => array( __( 'Request data selects an included file', 'shieldlume-security' ), __( 'A PHP include or require expression selects its source directly from request-controlled data.', 'shieldlume-security' ) ), 'php.request-callback' => array( __( 'Request data selects a callback', 'shieldlume-security' ), __( 'A PHP callback function is selected directly from request-controlled data.', 'shieldlume-security' ) ), 'php.executable-write' => array( __( 'A write targets an executable file', 'shieldlume-security' ), __( 'A PHP write targets an executable filename and directly includes request input or decoded data.', 'shieldlume-security' ) ), 'file.executable-upload' => array( __( 'The uploads directory contains executable PHP', 'shieldlume-security' ), __( 'A file beneath the verified uploads directory has an executable PHP extension and code beyond an empty access guard.', 'shieldlume-security' ) ), 'php.literal-execution-alias' => array( __( 'A function alias receives request data', 'shieldlume-security' ), __( 'A literal or safely decoded alias names a command-execution function and directly receives request-controlled data.', 'shieldlume-security' ) ), 'php.request-selected-function' => array( __( 'Request data selects a function to call', 'shieldlume-security' ), __( 'A function variable is selected from request data in the preceding statement and is then called.', 'shieldlume-security' ) ), 'js.browser-input-execution' => array( __( 'Browser input reaches dynamic JavaScript', 'shieldlume-security' ), __( 'A JavaScript execution expression combines decoding with browser-controlled input.', 'shieldlume-security' ) ), 'js.browser-input-dom' => array( __( 'Browser input reaches an HTML-writing function', 'shieldlume-security' ), __( 'Decoded browser-controlled data is passed directly to a document HTML-writing function.', 'shieldlume-security' ) ), 'js.browser-input-timer' => array( __( 'A timer receives decoded browser input', 'shieldlume-security' ), __( 'A JavaScript timer receives decoded browser-controlled text as its code argument.', 'shieldlume-security' ) ), ); } }