Plugin Directory

Changeset 3757543


Ignore:
Timestamp:
10/20/2026 10:55:53 PM (28 hours ago)
Author:
omwamij
Message:

TibaCheck 0.13.2: WooCommerce store pack, OWASP Top 10:2025 reporting, debug-display mapped to A10

Location:
tibacheck-site-audit/trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • tibacheck-site-audit/trunk/includes/checks/class-check-app-passwords.php

    r3755283 r3757543  
    3939                                'label'   => __( 'Application passwords', 'tibacheck-site-audit' ),
    4040                                'status'  => 'warn',
     41                                'evidence_complete' => false,
    4142                                'message' => __( 'The database did not answer the application-password lookup, so we cannot confirm whether any exist.', 'tibacheck-site-audit' ),
    4243                                'fix'     => __( 'Re-run the audit; if this persists, ask your host to check database access for the users tables.', 'tibacheck-site-audit' ),
  • tibacheck-site-audit/trunk/includes/checks/class-check-core-updates.php

    r3755283 r3757543  
    1515                $themes  = get_site_transient( 'update_themes' );
    1616
    17                 if ( false === $core && false === $plugins && false === $themes ) {
    18                         return array(
    19                                 'id'      => 'core-updates',
    20                                 'label'   => __( 'Software updates', 'tibacheck-site-audit' ),
    21                                 'status'  => 'warn',
    22                                 'message' => __( 'Update status unknown — WordPress has not completed an update check on this site yet, so we cannot confirm anything is current.', 'tibacheck-site-audit' ),
    23                                 'fix'     => __( 'Visit Dashboard → Updates once to trigger a check, then re-run this audit.', 'tibacheck-site-audit' ),
    24                         );
     17                // Each population must be known independently. Missing data is not zero updates.
     18                $unknown = array();
     19                $core_known = is_object( $core ) && isset( $core->updates ) && is_array( $core->updates ) && count( $core->updates ) > 0;
     20                if ( $core_known ) {
     21                        foreach ( $core->updates as $update ) {
     22                                if ( ! is_object( $update ) || ! isset( $update->response ) || ! is_string( $update->response ) || '' === $update->response ) {
     23                                        $core_known = false;
     24                                }
     25                        }
    2526                }
     27                if ( ! $core_known ) { $unknown[] = __( 'WordPress core', 'tibacheck-site-audit' ); }
     28                if ( ! is_object( $plugins ) || ! isset( $plugins->response ) || ! ( is_array( $plugins->response ) || is_object( $plugins->response ) ) ) {
     29                        $unknown[] = __( 'plugins', 'tibacheck-site-audit' );
     30                }
     31                if ( ! is_object( $themes ) || ! isset( $themes->response ) || ! ( is_array( $themes->response ) || is_object( $themes->response ) ) ) {
     32                        $unknown[] = __( 'themes', 'tibacheck-site-audit' );
     33                }
     34                $unknown_message = $unknown ? sprintf(
     35                        /* translators: %s: components with unavailable update data */
     36                        __( 'Update status unknown for: %s. Visit Dashboard → Updates to complete a check, then run the audit again.', 'tibacheck-site-audit' ),
     37                        implode( ', ', $unknown )
     38                ) : '';
    2639
    2740                $core_pending = false;
    28                 if ( $core && ! empty( $core->updates ) ) {
     41                if ( is_object( $core ) && isset( $core->updates ) && is_array( $core->updates ) ) {
    2942                        foreach ( (array) $core->updates as $u ) {
    30                                 if ( isset( $u->response ) && 'latest' !== $u->response ) {
     43                                if ( is_object( $u ) && isset( $u->response ) && is_string( $u->response ) && '' !== $u->response && 'latest' !== $u->response ) {
    3144                                        $core_pending = true;
    3245                                        break;
    … …  
    3548                }
    3649
    37                 $pcount = ( $plugins && ! empty( $plugins->response ) ) ? count( (array) $plugins->response ) : 0;
    38                 $tcount = ( $themes && ! empty( $themes->response ) ) ? count( (array) $themes->response ) : 0;
     50                $pcount = ( is_object( $plugins ) && isset( $plugins->response ) && ( is_array( $plugins->response ) || is_object( $plugins->response ) ) ) ? count( (array) $plugins->response ) : 0;
     51                $tcount = ( is_object( $themes ) && isset( $themes->response ) && ( is_array( $themes->response ) || is_object( $themes->response ) ) ) ? count( (array) $themes->response ) : 0;
    3952
    4053                $total = ( $core_pending ? 1 : 0 ) + $pcount + $tcount;
    … …  
    4356                        return array(
    4457                                'id'      => 'core-updates',
     58                                'evidence_complete' => empty( $unknown ),
    4559                                'label'   => __( 'Software updates', 'tibacheck-site-audit' ),
    46                                 'status'  => 'pass',
    47                                 'message' => __( 'Core, plugins, and themes are all up to date.', 'tibacheck-site-audit' ),
    48                                 'fix'     => __( 'Keep it that way — check weekly.', 'tibacheck-site-audit' ),
     60                                'status'  => $unknown ? 'warn' : 'pass',
     61                                'message' => $unknown ? $unknown_message : __( 'Core, plugins, and themes are all up to date.', 'tibacheck-site-audit' ),
     62                                'fix'     => $unknown ? __( 'Complete the missing update checks before treating this site as current.', 'tibacheck-site-audit' ) : __( 'Keep it that way — check weekly.', 'tibacheck-site-audit' ),
    4963                        );
    5064                }
    … …  
    6579                return array(
    6680                        'id'      => 'core-updates',
     81                                'evidence_complete' => empty( $unknown ),
    6782                        'label'   => __( 'Software updates', 'tibacheck-site-audit' ),
    6883                        'status'  => $core_pending ? 'fail' : 'warn',
    … …  
    7186                                __( 'Pending updates: %s.', 'tibacheck-site-audit' ),
    7287                                implode( ', ', $parts )
    73                         ),
     88                        ) . ( $unknown_message ? ' ' . $unknown_message : '' ),
    7489                        'fix'     => __( 'Back up first, then update — core first, then plugins, then themes.', 'tibacheck-site-audit' ),
    7590                );
  • tibacheck-site-audit/trunk/includes/checks/class-check-db-triggers.php

    r3755283 r3757543  
    3131                                'label'   => __( 'Database triggers', 'tibacheck-site-audit' ),
    3232                                'status'  => 'warn',
     33                                'evidence_complete' => false,
    3334                                'message' => __( 'The database did not answer the trigger inspection, so we cannot confirm this site has no database triggers.', 'tibacheck-site-audit' ),
    3435                                'fix'     => __( 'Ask your host to confirm the database user can read information_schema, then re-run the audit.', 'tibacheck-site-audit' ),
    … …  
    5960                                        'label'   => __( 'Database triggers', 'tibacheck-site-audit' ),
    6061                                        'status'  => 'warn',
     62                                'evidence_complete' => false,
    6163                                        'message' => __( 'We could not confirm trigger visibility for this database user, so an empty result here is not proof that no triggers exist.', 'tibacheck-site-audit' ),
    6264                                        'fix'     => __( 'Ask your host whether the database user holds the TRIGGER privilege (or can see information_schema.TRIGGERS), then re-run the audit.', 'tibacheck-site-audit' ),
  • tibacheck-site-audit/trunk/includes/checks/class-check-uploads-code.php

    r3755283 r3757543  
    6464                                        'label'   => __( 'Code in uploads', 'tibacheck-site-audit' ),
    6565                                        'status'  => 'warn',
     66                                'evidence_complete' => false,
    6667                                        'message' => __( 'The uploads directory could not be fully scanned (permissions or a filesystem error).', 'tibacheck-site-audit' ),
    6768                                        'fix'     => __( 'Ask your host to check the uploads folder permissions, then re-run the audit.', 'tibacheck-site-audit' ),
    … …  
    7778                                'label'   => __( 'Code in uploads', 'tibacheck-site-audit' ),
    7879                                'status'  => 'fail',
     80                                'evidence_complete' => ! $truncated,
    7981                                'message' => sprintf(
    8082                                        /* translators: %s: list of code file paths found inside the uploads directory */
    … …  
    9193                                'label'   => __( 'Code in uploads', 'tibacheck-site-audit' ),
    9294                                'status'  => 'warn',
     95                                'evidence_complete' => false,
    9396                                'message' => sprintf(
    9497                                        /* translators: %d: number of files actually scanned before the safety limit */
  • tibacheck-site-audit/trunk/includes/class-tibacheck.php

    r3755283 r3757543  
    3838                'Tiba_Check_App_Passwords',
    3939                'Tiba_Check_Plugin_Redundancy',
     40                // WooCommerce pack: each of these returns null (no row) when
     41                // WooCommerce is not active, so non-store reports never see them.
     42                'Tiba_Check_Woo_Pages',
     43                'Tiba_Check_Woo_Payments',
     44                'Tiba_Check_Woo_Details',
     45                'Tiba_Check_Woo_Checkout',
    4046        );
    4147
    … …  
    6268                                $check  = new $class();
    6369                                $result = $check->run();
     70                                if ( null === $result && self::allows_null( $class ) ) {
     71                                        // A site-conditional check declared itself not
     72                                        // applicable to this site (e.g. a store check
     73                                        // with no store): omit it entirely — no row,
     74                                        // no score effect. Nulls from any other check
     75                                        // fall through to the unreadable-result warning
     76                                        // below, exactly as before this contract existed,
     77                                        // so a check can never vanish silently by bug.
     78                                        continue;
     79                                }
    6480                                if ( self::is_valid_result( $result ) ) {
    6581                                        $results[] = $result;
    … …  
    118134
    119135        /**
    120          * OWASP Top 10 (2021) mapping: check id => category code.
     136         * The only checks allowed to return null ("not applicable").
     137         * Kept explicit so the null contract can never silently
     138         * swallow a check that was supposed to report (see run()).
     139         *
     140         * @param string $class Check class name.
     141         * @return bool
     142         */
     143        private static function allows_null( $class ) {
     144                return in_array(
     145                        $class,
     146                        array(
     147                                'Tiba_Check_Woo_Pages',
     148                                'Tiba_Check_Woo_Payments',
     149                                'Tiba_Check_Woo_Details',
     150                                'Tiba_Check_Woo_Checkout',
     151                        ),
     152                        true
     153                );
     154        }
     155
     156        /**
     157         * Whether WooCommerce is active on this site (site-level or
     158         * network-wide on multisite). Two signals, either suffices:
     159         * the canonical plugin path in the active-plugins options
     160         * (works in any context, admin or cron), or WooCommerce's
     161         * own class being loaded (catches renamed folders and
     162         * must-use/composer loading, where the path test would miss
     163         * a real store).
     164         *
     165         * @return bool
     166         */
     167        public static function woocommerce_active() {
     168                return in_array( 'woocommerce/woocommerce.php', self::active_plugin_paths(), true )
     169                        || class_exists( 'WooCommerce' );
     170        }
     171
     172        /**
     173         * OWASP Top 10 (2025) mapping: check id => category code.
    121174         * Only categories our checks genuinely assess are mapped; the rest
    122175         * are reported as "not assessed" rather than implied safe.
    … …  
    126179        public static function owasp_map() {
    127180                return array(
    128                         'core-updates'        => 'A06',
    129                         'php-version'         => 'A06',
    130                         'inactive-plugins'    => 'A06',
     181                        'core-updates'        => 'A03',
     182                        'php-version'         => 'A03',
     183                        'inactive-plugins'    => 'A03',
    131184                        'user-accounts'       => 'A01',
    132185                        'registration-safety' => 'A01',
    133                         'file-editor'         => 'A05',
    134                         'debug-display'       => 'A05',
    135                         'ssl'                 => 'A02',
     186                        'file-editor'         => 'A02',
     187                        'debug-display'       => 'A10',
     188                        'ssl'                 => 'A04',
    136189                        'privilege-audit'     => 'A01',
    137190                        'app-passwords'       => 'A07',
    … …  
    139192                        'db-triggers'         => 'A08',
    140193                        'cron-provenance'     => 'A08',
    141                         'uploads-code'        => 'A05',
    142                         'prepend-directives'  => 'A05',
    143                 );
    144         }
    145 
    146         /**
    147          * All ten OWASP Top 10 (2021) categories, code => name.
     194                        'uploads-code'        => 'A02',
     195                        'prepend-directives'  => 'A02',
     196                );
     197        }
     198
     199        /**
     200         * All ten OWASP Top 10 (2025) categories, code => name.
    148201         *
    149202         * @return array<string,string>
    … …  
    152205                return array(
    153206                        'A01' => __( 'Broken Access Control', 'tibacheck-site-audit' ),
    154                         'A02' => __( 'Cryptographic Failures', 'tibacheck-site-audit' ),
    155                         'A03' => __( 'Injection', 'tibacheck-site-audit' ),
    156                         'A04' => __( 'Insecure Design', 'tibacheck-site-audit' ),
    157                         'A05' => __( 'Security Misconfiguration', 'tibacheck-site-audit' ),
    158                         'A06' => __( 'Vulnerable and Outdated Components', 'tibacheck-site-audit' ),
    159                         'A07' => __( 'Identification and Authentication Failures', 'tibacheck-site-audit' ),
    160                         'A08' => __( 'Software and Data Integrity Failures', 'tibacheck-site-audit' ),
    161                         'A09' => __( 'Security Logging and Monitoring Failures', 'tibacheck-site-audit' ),
    162                         'A10' => __( 'Server-Side Request Forgery', 'tibacheck-site-audit' ),
    163                 );
     207                        'A02' => __( 'Security Misconfiguration', 'tibacheck-site-audit' ),
     208                        'A03' => __( 'Software Supply Chain Failures', 'tibacheck-site-audit' ),
     209                        'A04' => __( 'Cryptographic Failures', 'tibacheck-site-audit' ),
     210                        'A05' => __( 'Injection', 'tibacheck-site-audit' ),
     211                        'A06' => __( 'Insecure Design', 'tibacheck-site-audit' ),
     212                        'A07' => __( 'Authentication Failures', 'tibacheck-site-audit' ),
     213                        'A08' => __( 'Software or Data Integrity Failures', 'tibacheck-site-audit' ),
     214                        'A09' => __( 'Security Logging & Alerting Failures', 'tibacheck-site-audit' ),
     215                        'A10' => __( 'Mishandling of Exceptional Conditions', 'tibacheck-site-audit' ),
     216                );
     217        }
     218
     219        /** Labels describe limited checks, never the whole risk category. */
     220        public static function owasp_status_label( $status ) {
     221                $labels = array(
     222                        'pass' => __( 'Partial — checks passed', 'tibacheck-site-audit' ),
     223                        'warn' => __( 'Partial — checks need attention', 'tibacheck-site-audit' ),
     224                        'fail' => __( 'Partial — checks failed', 'tibacheck-site-audit' ),
     225                        'unknown' => __( 'Incomplete evidence', 'tibacheck-site-audit' ),
     226                );
     227                return $labels[ $status ] ?? $labels['unknown'];
    164228        }
    165229
    … …  
    167231         * Compute the two headline scores from a result set.
    168232         * Health: every check, fail −10 / warn −4.
    169          * Security: OWASP-mapped checks only, fail −20 / warn −8.
     233         * Security: OWASP-mapped checks only, fail −20 / warn −8; null if any mapped check is missing or invalid.
    170234         * Both clamp to 0–100. Also returns the worst status per OWASP
    171235         * category, keyed by category code.
    … …  
    180244                $owasp    = array();
    181245                $map      = self::owasp_map();
    182                 $mapped   = 0;
     246                $mapped   = array();
    183247
    184248                foreach ( $results as $r ) {
    … …  
    192256                        }
    193257                        if ( isset( $map[ $r['id'] ] ) ) {
    194                                 $mapped++;
     258                                if ( ! isset( $r['evidence_complete'] ) || true === $r['evidence_complete'] ) {
     259                                        $mapped[ $r['id'] ] = true;
     260                                }
    195261                                if ( 'fail' === $r['status'] ) {
    196262                                        $security -= 20;
    … …  
    205271                }
    206272
     273                // Every mapped check is required for the numeric security heuristic.
     274                // A runner fallback has a different ID, so missing/failed execution
     275                // cannot silently become a perfect score or a passing category.
     276                foreach ( $map as $id => $code ) {
     277                        if ( ! isset( $mapped[ $id ] ) && ( ! isset( $owasp[ $code ] ) || 'pass' === $owasp[ $code ] ) ) {
     278                                $owasp[ $code ] = 'unknown';
     279                        }
     280                }
     281
    207282                return array(
    208283                        'health'   => max( 0, $health ),
    209                         'security' => $mapped ? max( 0, $security ) : null,
     284                        'security' => count( $mapped ) === count( $map ) ? max( 0, $security ) : null,
     285                        'owasp_version' => '2025',
    210286                        'owasp'    => $owasp,
    211287                );
  • tibacheck-site-audit/trunk/includes/class-watch.php

    r3755283 r3757543  
    139139                                'health'   => $scores['health'],
    140140                                'security' => $scores['security'],
     141                                'owasp_version' => $scores['owasp_version'],
    141142                                'time'     => time(),
    142143                                'statuses' => $statuses,
  • tibacheck-site-audit/trunk/includes/interface-check.php

    r3755283 r3757543  
    88 *   - status:  'pass' | 'warn' | 'fail'
    99 *   - message: what was found, in plain language
     10 *   - evidence_complete: optional bool; false for unavailable or truncated evidence, independently of severity
    1011 *   - fix:     what to do about it, in plain language
    1112 *
    1213 * Checks must be read-only: they may not change options, files, or the
    1314 * database. The audit reports; the care plan (or the site owner) fixes.
     15 *
     16 * A check that does not apply to this site (for example a store check
     17 * on a site with no store) returns null instead of a result row; the
     18 * runner then omits it entirely — no row, no score effect. Only
     19 * checks on the runner's explicit site-conditional list may use
     20 * this; a null from any other check is treated as an unreadable
     21 * result and surfaces as a warning row, so no check can vanish
     22 * silently by accident.
    1423 */
    1524
    … …  
    1827interface Tiba_Check_Interface {
    1928        /**
    20          * @return array{id:string,label:string,status:string,message:string,fix:string}
     29         * @return array{id:string,label:string,status:string,message:string,fix:string}|null
     30         *         A result row, or null when the check does not apply to this site.
    2131         */
    2232        public function run();
  • tibacheck-site-audit/trunk/readme.txt

    r3755336 r3757543  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 0.12.1
     7Stable tag: 0.13.2
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1010
    11 Security & health audit in one click: OWASP-mapped security score, backdoor hunt, hidden accounts, plugin review by job. 23 read-only checks.
     11One-click security & health audit: OWASP-mapped scores, hidden-account and plugin reviews, 23 read-only checks, plus a WooCommerce store pack.
    1212
    1313== Description ==
    1414
    15 TibaCheck runs a read-only audit of your site's configuration and shows you, in plain language, what needs attention. Every report leads with two scores — a **Site Health Score** and a **Security Score** built from checks mapped to the **OWASP Top 10 (2021)** — with a full OWASP coverage table showing what was assessed and what needs an external deep scan:
     15TibaCheck runs a read-only audit of your site's configuration and shows you, in plain language, what needs attention. Every report leads with two scores — a **Site Health Score** and a **Security Score** built from checks mapped to the **OWASP Top 10 (2025)** — with a full OWASP coverage table showing what was assessed and what needs an external deep scan:
    1616
    1717* **Software updates** — pending WordPress core, plugin, and theme updates (outdated software is the #1 way sites get hacked)
    … …  
    4141* **Application passwords** — an inventory of the standing credentials that bypass your login screen, with the oldest one's age
    4242
     43Plus the **WooCommerce store pack** — four more checks that appear only when WooCommerce is active, so they never clutter a non-store report. A store that cannot take payment is down in the only way that matters, and these are what a care plan watches on a store every month:
     44
     45* **Store pages** — the Checkout page must be assigned and published or customers cannot buy at all (the check fails when it is not); the Cart, Shop, and My Account pages are checked too and warn when broken
     46* **Payment gateways** — at least one gateway must be switched on, read from WooCommerce's own gateway registry so extension gateways count exactly like built-in ones; up to four enabled ones are named
     47* **Store details** — store currency, base country, and street address, the details taxes, shipping, and invoices are calculated from (a currency or country that was never set is flagged with the default WooCommerce silently uses instead)
     48* **Checkout posture** — the silent sales leak: guest checkout off, no account creation during checkout, *and* registration closed on the My Account page too, so only existing account holders can buy. Checkout encryption itself is graded once, by the SSL / HTTPS check, and cross-referenced here — never double-counted
     49
    4350Every report also carries a **Plugins Review** section: every installed plugin grouped by the job it does — backups, security, SEO, speed, forms, builders, store, analytics, spam, media, email, languages, utilities — each with a one-line purpose and its active/inactive state. When two or more *active* plugins share a job, the group is flagged **repetitive** and a Plugin Overlap check warns: duplicates conflict, slow the site, and double what you must keep updated. Plugins we don't recognize are listed honestly as "not yet categorized" — we never guess what a plugin does.
    4451
    … …  
    6572
    6673= Is the OWASP score a certification? =
    67 No. The Security Score maps this plugin's checks to the OWASP Top 10 (2021) categories they assess, and the coverage table states plainly which categories a plugin cannot assess from inside (injection in custom code, insecure design, SSRF, and others). Those require an external assessment — which is part of the WP Care Plan deep scan.
     74No. The Security check score maps this plugin's checks to the OWASP Top 10 (2025) categories they assess, and the coverage table labels every mapped category as partial. A passing check does not establish category-wide security. Injection, insecure design and logging/alerting remain unassessed. The score is a TibaCheck heuristic, not an OWASP certification, and is withheld when mapped evidence is incomplete. Separate code, architecture or dynamic assessment is required.
    6875
    6976= What do the statuses mean? =
    … …  
    7279== Screenshots ==
    7380
    74 1. One-click audit results — Site Health and Security score cards, tabbed Health Check / Security Scan results, and color-coded status pills at a glance.
    75 2. Security Scan tab — Health and OWASP-mapped Security scores with the OWASP Top 10 (2021) coverage table: what was assessed, and what needs an external deep scan, stated plainly.
    76 3. Plugins Review — every installed plugin grouped by the job it does, with repetitive groups flagged when two or more active plugins do the same job.
    77 4. Printable client report — a branded audit document with the score cards, every check and its detail, and the plugin and OWASP summaries, ready to print or save as a PDF for a client.
    78 
     811. One-click audit results with color-coded status pills and a summary bar — passing, needs attention, and failing at a glance.
    7982
    8083== Changelog ==
     84
     85= 0.13.2 =
     86* Coverage correction: the debug display check now maps to OWASP A10:2025 — Mishandling of Exceptional Conditions (error output shown to visitors is an error-handling failure). Assessed coverage is now 7 of 10 categories; A02 keeps the file editor, uploads code and prepend directive checks.
     87* Fixed the wordpress.org short description being truncated (now within the 150-character limit).
     88
     89= 0.13.1 =
     90* Reports use OWASP Top 10:2025 with partial coverage labels, explicit unassessed risks and evidence-completeness safeguards.
     91* Missing or unavailable mapped evidence withholds the security check score; model-versioned snapshots avoid incompatible historical score comparisons.
     92* The printable client report now shares the dashboard coverage table.
     93
     94
     95= 0.13.0 =
     96* NEW: the **WooCommerce store pack** — four checks that run
     97  only when WooCommerce is active (on any other site they do
     98  not appear at all): **Store pages** (Cart/Checkout assigned
     99  and published, or the store cannot sell — plus Shop and
     100  My Account), **Payment gateways** (at least one gateway
     101  switched on, read from WooCommerce's own registry so
     102  extension gateways count; up to four enabled gateways are
     103  named, and an unreadable registry reports "could not
     104  confirm" instead of guessing), **Store details** (currency,
     105  base country, and street address — what taxes, shipping,
     106  and invoices are calculated from), and **Checkout posture**
     107  (the guest-checkout/account-creation/registration
     108  combination that silently locks out new customers;
     109  checkout encryption stays graded once, by the SSL check).
     110  The
     111  audit now runs twenty-three checks, twenty-seven on a
     112  WooCommerce store.
     113* Hidden client reports now set their WordPress admin page titles before the header renders, avoiding blank titles and PHP deprecations.
     114* Missing or malformed update data for any component now remains unknown rather than producing an all-current result; known pending updates remain visible.
    81115
    82116= 0.12.1 =
  • tibacheck-site-audit/trunk/tibacheck.php

    r3755283 r3757543  
    33 * Plugin Name:       TibaCheck — Site Audit
    44 * Description:       One-click site health and security audit: outdated software, PHP version, inactive plugins, and more. Know what's wrong before it breaks.
    5  * Version:           0.12.1
     5 * Version:           0.13.2
    66 * Requires at least: 6.0
    77 * Requires PHP:      7.4
    … …  
    1818defined( 'ABSPATH' ) || exit;
    1919
    20 define( 'TIBA_CHECK_VERSION', '0.12.1' );
     20define( 'TIBA_CHECK_VERSION', '0.13.2' );
    2121define( 'TIBA_CHECK_PATH', plugin_dir_path( __FILE__ ) );
    2222
    … …  
    4747require_once TIBA_CHECK_PATH . 'includes/class-plugin-catalog.php';
    4848require_once TIBA_CHECK_PATH . 'includes/checks/class-check-plugin-redundancy.php';
     49require_once TIBA_CHECK_PATH . 'includes/checks/class-check-woo-pages.php';
     50require_once TIBA_CHECK_PATH . 'includes/checks/class-check-woo-payments.php';
     51require_once TIBA_CHECK_PATH . 'includes/checks/class-check-woo-details.php';
     52require_once TIBA_CHECK_PATH . 'includes/checks/class-check-woo-checkout.php';
    4953require_once TIBA_CHECK_PATH . 'includes/class-watch.php';
    5054
    … …  
    8185        );
    8286        // Hidden page: the printable client report (see below).
    83         add_submenu_page(
    84                 null,
     87        $report_hook = add_submenu_page(
     88                '',
    8589                __( 'Site Audit Report', 'tibacheck-site-audit' ),
    8690                __( 'Site Audit Report', 'tibacheck-site-audit' ),
    … …  
    8993                'tiba_check_render_client_report'
    9094        );
     95        if ( $report_hook ) {
     96                add_action( 'load-' . $report_hook, static function () {
     97                        global $title;
     98                        $title = __( 'Site Audit Report', 'tibacheck-site-audit' );
     99                } );
     100        }
    91101}
    92102add_action( 'admin_menu', 'tiba_check_admin_menu' );
    … …  
    237247                        <div>
    238248                                <h1><?php esc_html_e( 'TibaCheck Site Audit', 'tibacheck-site-audit' ); ?></h1>
    239                                 <p class="tibacheck-tagline"><strong><?php esc_html_e( 'Know what\'s wrong before it breaks.', 'tibacheck-site-audit' ); ?></strong> <?php esc_html_e( 'One click runs twenty-three checks — updates, PHP, every plugin grouped by job, backups, accounts, security settings, visibility, bloat, and a backdoor hunt most audits skip.', 'tibacheck-site-audit' ); ?></p>
     249                                <p class="tibacheck-tagline"><strong><?php esc_html_e( 'Know what\'s wrong before it breaks.', 'tibacheck-site-audit' ); ?></strong> <?php esc_html_e( 'One click runs twenty-three checks — updates, PHP, every plugin grouped by job, backups, accounts, security settings, visibility, bloat, and a backdoor hunt most audits skip. WooCommerce stores get four extra store checks.', 'tibacheck-site-audit' ); ?></p>
    240250                        </div>
    241251                </div>
    … …  
    289299                                        'health'   => $scores['health'],
    290300                                        'security' => $scores['security'],
     301                                        'owasp_version' => $scores['owasp_version'],
    291302                                        'time'     => time(),
    292303                                        'statuses' => $statuses,
    … …  
    329340                                <div class="tibacheck-score">
    330341                                        <div class="tibacheck-score-num" style="color:<?php echo esc_attr( $band_color[ Tiba_Check::score_band( $scores['security'] ) ] ); ?>"><?php echo esc_html( $scores['security'] ); ?><span style="font-size:16px;color:#787c82">/100</span></div>
    331                                         <div class="tibacheck-score-label"><?php esc_html_e( 'Security score — OWASP-mapped checks', 'tibacheck-site-audit' ); ?></div>
    332                                         <?php if ( is_array( $prev ) && isset( $prev['security'], $prev['time'] ) && null !== $prev['security'] ) : ?>
     342                                        <div class="tibacheck-score-label"><?php esc_html_e( 'Security check score — partial coverage', 'tibacheck-site-audit' ); ?></div>
     343                                        <?php if ( is_array( $prev ) && isset( $prev['security'], $prev['time'] ) && null !== $prev['security'] && ( $prev['owasp_version'] ?? '' ) === $scores['owasp_version'] ) : ?>
    333344                                                <div class="tibacheck-delta"><?php echo esc_html( sprintf( '%+d', $scores['security'] - (int) $prev['security'] ) ); ?> <?php esc_html_e( 'since', 'tibacheck-site-audit' ); ?> <?php echo esc_html( date_i18n( 'M j', (int) $prev['time'] ) ); ?></div>
    334345                                        <?php endif; ?>
    … …  
    352363                                        <?php tiba_check_render_checks_table( $tab_rows['security'] ); ?>
    353364                                        <h3 style="margin:20px 0 6px"><?php esc_html_e( 'OWASP Top 10 coverage', 'tibacheck-site-audit' ); ?></h3>
    354                                         <p class="description" style="margin-bottom:10px"><?php esc_html_e( 'How this audit maps to the OWASP Top 10 (2021). Categories a plugin cannot honestly assess from inside are marked — those are covered by the Care Plan deep scan, an external assessment.', 'tibacheck-site-audit' ); ?></p>
    355                                         <div class="tibacheck-results">
    356                                         <table class="widefat striped">
    357                                                 <thead><tr>
    358                                                         <th><?php esc_html_e( 'Category', 'tibacheck-site-audit' ); ?></th>
    359                                                         <th><?php esc_html_e( 'Status', 'tibacheck-site-audit' ); ?></th>
    360                                                         <th><?php esc_html_e( 'Basis', 'tibacheck-site-audit' ); ?></th>
    361                                                 </tr></thead>
    362                                                 <tbody>
    363                                                 <?php
    364                                                 $owasp_map = Tiba_Check::owasp_map();
    365                                                 $basis     = array();
    366                                                 foreach ( $results as $r ) {
    367                                                         if ( isset( $owasp_map[ $r['id'] ] ) ) {
    368                                                                 $basis[ $owasp_map[ $r['id'] ] ][] = $r['label'];
    369                                                         }
    370                                                 }
    371                                                 foreach ( Tiba_Check::owasp_categories() as $code => $name ) :
    372                                                         ?>
    373                                                         <tr>
    374                                                                 <td><strong><?php echo esc_html( $code ); ?></strong> — <?php echo esc_html( $name ); ?></td>
    375                                                                 <?php if ( isset( $scores['owasp'][ $code ] ) ) : ?>
    376                                                                         <td><span class="tibacheck-pill tibacheck-pill--<?php echo esc_attr( $scores['owasp'][ $code ] ); ?>"><?php echo esc_html( strtoupper( $scores['owasp'][ $code ] ) ); ?></span></td>
    377                                                                         <td><?php
    378                                                                                 echo esc_html(
    379                                                                                         sprintf(
    380                                                                                                 /* translators: %s: comma-separated check names */
    381                                                                                                 __( 'Assessed by: %s', 'tibacheck-site-audit' ),
    382                                                                                                 implode( ', ', $basis[ $code ] )
    383                                                                                         )
    384                                                                                 );
    385                                                                         ?></td>
    386                                                                 <?php else : ?>
    387                                                                         <td><span style="color:#787c82"><?php esc_html_e( 'Not assessed', 'tibacheck-site-audit' ); ?></span></td>
    388                                                                         <td><span style="color:#787c82"><?php esc_html_e( 'Covered by the Care Plan deep scan (external OWASP assessment).', 'tibacheck-site-audit' ); ?></span></td>
    389                                                                 <?php endif; ?>
    390                                                         </tr>
    391                                                 <?php endforeach; ?>
    392                                                 </tbody>
    393                                         </table>
    394                                         </div>
     365                                        <p class="description" style="margin-bottom:10px"><?php esc_html_e( 'OWASP Top 10 (2025): mapped checks provide partial coverage only. A passed check does not establish that a category or site is secure. The security score is a TibaCheck heuristic, not an OWASP score or certification; it is unavailable when mapped evidence is incomplete. Unassessed risks require a separate code, architecture or dynamic assessment.', 'tibacheck-site-audit' ); ?></p>
     366                                        <?php tiba_check_render_owasp_coverage( $results, $scores ); ?>
    395367                                </div>
    396368                                <div class="tibacheck-panel tibacheck-panel--plugins">
    … …  
    500472                                <div class="tcr-card"><div class="n"><?php echo esc_html( $scores['health'] ); ?>/100</div><div class="l"><?php esc_html_e( 'Site health score', 'tibacheck-site-audit' ); ?></div></div>
    501473                                <?php if ( null !== $scores['security'] ) : ?>
    502                                 <div class="tcr-card"><div class="n"><?php echo esc_html( $scores['security'] ); ?>/100</div><div class="l"><?php esc_html_e( 'Security score (OWASP-mapped)', 'tibacheck-site-audit' ); ?></div></div>
     474                                <div class="tcr-card"><div class="n"><?php echo esc_html( $scores['security'] ); ?>/100</div><div class="l"><?php esc_html_e( 'Security check score (partial coverage)', 'tibacheck-site-audit' ); ?></div></div>
    503475                                <?php endif; ?>
    504476                                <div class="tcr-card"><div class="n"><?php echo esc_html( count( $results ) ); ?></div><div class="l"><?php esc_html_e( 'Checks run', 'tibacheck-site-audit' ); ?></div></div>
    … …  
    556528                                        sprintf(
    557529                                                /* translators: %s: comma-separated OWASP category list */
    558                                                 __( 'The security checks map to these OWASP Top 10 (2021) categories: %s. The remaining categories need authenticated or dynamic testing, which a read-only audit does not perform.', 'tibacheck-site-audit' ),
     530                                                __( 'The security checks map to these OWASP Top 10 (2025) categories: %s. Coverage is partial in every mapped category. Passing checks do not establish category-wide security; unassessed risks require a separate code, architecture or dynamic assessment.', 'tibacheck-site-audit' ),
    559531                                                implode( ', ', $names )
    560532                                        )
    561533                                );
    562534                        ?></p>
     535                        <p><?php esc_html_e( 'The security score is a TibaCheck heuristic, not an OWASP score or certification. It is unavailable when mapped evidence is incomplete.', 'tibacheck-site-audit' ); ?></p>
     536                        <?php tiba_check_render_owasp_coverage( $results, $scores ); ?>
    563537                        <h2><?php esc_html_e( '5. Scope and method', 'tibacheck-site-audit' ); ?></h2>
    564                         <p><?php esc_html_e( 'TibaCheck runs twenty-three read-only checks over updates, PHP, accounts, security settings, backups, site bloat, and persistence mechanisms that planted backdoors rely on. It changes nothing on the site, makes no external requests, and stores only the audit scores, per-check statuses, time, any pending weekly Watch change alert, and the time of the last automatic Watch run. This report was generated fresh when it was opened.', 'tibacheck-site-audit' ); ?></p>
     538                        <p><?php esc_html_e( 'TibaCheck runs twenty-three read-only checks over updates, PHP, accounts, security settings, backups, site bloat, and persistence mechanisms that planted backdoors rely on. On a WooCommerce store it runs four more — store pages, payment gateways, store details, and checkout posture. It changes nothing on the site, makes no external requests, and stores only the audit scores, per-check statuses, time, any pending weekly Watch change alert, and the time of the last automatic Watch run. This report was generated fresh when it was opened.', 'tibacheck-site-audit' ); ?></p>
    565539                        <h2><?php esc_html_e( '6. Recommended next steps', 'tibacheck-site-audit' ); ?></h2>
    566540                        <p><?php esc_html_e( 'Items needing attention are a work list, not a verdict: fix them directly, or have them handled. TibaCheck care plans watch every one of these checks monthly — updates, backups, accounts, and the backdoor hunt — and send a report like this one each month.', 'tibacheck-site-audit' ); ?></p>
    … …  
    571545        <?php
    572546}
     547
     548/** Shared free dashboard/print coverage; caller supplies the same audit snapshot. */
     549function tiba_check_render_owasp_coverage( $results, $scores ) {
     550    ?>
     551                                        <div class="tibacheck-results">
     552                                        <table class="widefat striped">
     553                                                <thead><tr>
     554                                                        <th><?php esc_html_e( 'Category', 'tibacheck-site-audit' ); ?></th>
     555                                                        <th><?php esc_html_e( 'Status', 'tibacheck-site-audit' ); ?></th>
     556                                                        <th><?php esc_html_e( 'Basis', 'tibacheck-site-audit' ); ?></th>
     557                                                </tr></thead>
     558                                                <tbody>
     559                                                <?php
     560                                                $owasp_map = Tiba_Check::owasp_map();
     561                                                $basis     = array();
     562                                                foreach ( $results as $r ) {
     563                                                        if ( isset( $owasp_map[ $r['id'] ] ) ) {
     564                                                                $basis[ $owasp_map[ $r['id'] ] ][] = $r['label'];
     565                                                        }
     566                                                }
     567                                                foreach ( Tiba_Check::owasp_categories() as $code => $name ) :
     568                                                        ?>
     569                                                        <tr>
     570                                                                <td><strong><?php echo esc_html( $code ); ?></strong> — <?php echo esc_html( $name ); ?></td>
     571                                                                <?php if ( isset( $scores['owasp'][ $code ] ) ) : ?>
     572                                                                        <td><span class="tibacheck-pill tibacheck-pill--<?php echo esc_attr( $scores['owasp'][ $code ] ); ?>"><?php echo esc_html( Tiba_Check::owasp_status_label( $scores['owasp'][ $code ] ) ); ?></span></td>
     573                                                                        <td><?php
     574                                                                                echo esc_html(
     575                                                                                        sprintf(
     576                                                                                                /* translators: %s: comma-separated check names */
     577                                                                                                __( 'Limited checks: %s', 'tibacheck-site-audit' ),
     578                                                                                                implode( ', ', $basis[ $code ] ?? array( __( 'No usable mapped check results.', 'tibacheck-site-audit' ) ) )
     579                                                                                        )
     580                                                                                );
     581                                                                        ?></td>
     582                                                                <?php else : ?>
     583                                                                        <td><span style="color:#787c82"><?php esc_html_e( 'Not assessed', 'tibacheck-site-audit' ); ?></span></td>
     584                                                                        <td><span style="color:#787c82"><?php esc_html_e( 'Requires a separate code, architecture or dynamic assessment.', 'tibacheck-site-audit' ); ?></span></td>
     585                                                                <?php endif; ?>
     586                                                        </tr>
     587                                                <?php endforeach; ?>
     588                                                </tbody>
     589                                        </table>
     590                                        </div>
     591    <?php
     592}
Note: See TracChangeset for help on using the changeset viewer.