Making WordPress.org

Changeset 6543


Ignore:
Timestamp:
02/05/2018 07:23:06 PM (7 years ago)
Author:
obenland
Message:

Plugins: Add basic set of plugin checks

Props Ipstenu.
Fixes #2350.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/admin/metabox/class-review-tools.php

    r6287 r6543  
    11<?php
     2/**
     3 * The Plugin Review metabox.
     4 *
     5 * @package WordPressdotorg\Plugin_Directory\Admin\Metabox
     6 */
     7
    28namespace WordPressdotorg\Plugin_Directory\Admin\Metabox;
    39
     
    1016 */
    1117class Review_Tools {
    12     static function display() {
    13         $post = get_post();
     18    /**
     19     * Contains all flags.
     20     *
     21     * @var array
     22     */
     23    public static $flagged = [
     24        'low'  => [],
     25        'med'  => [],
     26        'high' => [],
     27    ];
     28
     29    /**
     30     * List of commonly abused/misused terms.
     31     *
     32     * @var array
     33     */
     34    public static $reserved_slugs = [
     35        'wordpress',
     36        'woocommerce',
     37        'google',
     38        'youtube',
     39        'twitter',
     40        'facebook',
     41        'yoast',
     42        'jetpack',
     43    ];
     44
     45    /**
     46     * List of restricted plugin slugs.
     47     *
     48     * @var array
     49     */
     50    public static $restricted_slugs = [
     51        // High-value plugin genres due to their popularity, often abused by spammers.
     52        'gallery',
     53        'lightbox',
     54        'sitemap',
     55        'bookmark',
     56        'social',
     57        'cookie',
     58        'slide',
     59        'seo',
     60
     61        // Plugins we generally don't allow.
     62        'autoblog',
     63        'auto-blog',
     64        'booking',
     65        'plugin',
     66        'spinning',
     67        'framework',
     68    ];
     69
     70    /**
     71     * List of suspicious URLs.
     72     *
     73     * @var array
     74     */
     75    public static $weird_urls = [
     76        'blogger',
     77        'blogspot',
     78        'example.com',
     79        'weebly',
     80        'squarespace',
     81        'medium.com',
     82        'yahoo.com',
     83        'mail.com',
     84        'example.org',
     85    ];
     86
     87    /**
     88     * Displays links to plugin assets and automated flags.
     89     */
     90    public static function display() {
     91        $post   = get_post();
     92        $author = get_user_by( 'id', $post->post_author );
     93        $slug   = $post->post_name;
     94
     95        $author_commit = Tools::get_users_write_access_plugins( $author );
     96        // phpcs:ignore WordPress.VIP.RestrictedFunctions.get_posts_get_posts
     97        $author_plugins = get_posts( [
     98            'author'           => $author->ID,
     99            'post_type'        => 'plugin',
     100            'post__not_in'     => [ $post->ID ],
     101            'suppress_filters' => false,
     102        ] );
    14103
    15104        $zip_files = array();
     
    17106            $zip_files[ $zip_file->post_date ] = array( wp_get_attachment_url( $zip_file->ID ), $zip_file );
    18107        }
    19         uksort( $zip_files, function( $a, $b ) {
     108        uksort( $zip_files, function ( $a, $b ) {
    20109            return strtotime( $a ) < strtotime( $b );
    21110        } );
    22111
    23         if ( $zip_url = get_post_meta( $post->ID, '_submitted_zip', true ) ) {
     112        $zip_url = get_post_meta( $post->ID, '_submitted_zip', true );
     113        if ( $zip_url ) {
    24114            // Back-compat only.
    25115            $zip_files['User provided URL'] = array( $zip_url, null );
    26116        }
    27117
     118        echo '<p><strong>Zip files:</strong></p>';
     119        echo '<ul class="plugin-zip-files">';
    28120        foreach ( $zip_files as $zip_date => $zip ) {
    29121            list( $zip_url, $zip_file ) = $zip;
    30             $zip_size                   = ( is_object( $zip_file ) ? size_format( filesize( get_attached_file( $zip_file->ID ) ), 1 ) : __( 'unknown size', 'wporg-plugins' ) );
    31             printf(
    32                 '<p>' . __( '<strong>Zip file:</strong> %s', 'wporg-plugins' ) . '</p>',
    33                 sprintf( '%s <a href="%s">%s</a> (%s)', esc_html( $zip_date ), esc_url( $zip_url ), esc_html( $zip_url ), esc_html( $zip_size ) )
    34             );
    35         }
    36 
    37         if ( 'new' !== $post->post_status && 'pending' !== $post->post_status ) {
    38             echo "<ul>
    39                 <li><a href='https://plugins.trac.wordpress.org/log/{$post->post_name}/'>" . __( 'Development Log', 'wporg-plugins' ) . "</a></li>
    40                 <li><a href='https://plugins.svn.wordpress.org/{$post->post_name}/'>" . __( 'Subversion Repository', 'wporg-plugins' ) . "</a></li>
    41                 <li><a href='https://plugins.trac.wordpress.org/browser/{$post->post_name}/'>" . __( 'Browse in Trac', 'wporg-plugins' ) . '</a></li>
    42             </ul>';
    43         }
    44         if ( $post->post_excerpt && in_array( $post->post_status, array( 'new', 'pending', 'approved' ) ) ) {
    45             echo '<p>' . strip_tags( $post->post_excerpt ) . '</p>';
    46         }
    47 
    48         add_filter( 'wp_comment_reply', function( $string ) use ( $post ) {
    49             $author = get_user_by( 'id', $post->post_author );
    50 
     122            $zip_size                   = is_object( $zip_file ) ? size_format( filesize( get_attached_file( $zip_file->ID ) ), 1 ) : 'unknown size';
     123
     124            printf( '<li>%s <a href="%s">%s</a> (%s)</li>', esc_html( $zip_date ), esc_url( $zip_url ), esc_html( $zip_url ), esc_html( $zip_size ) );
     125        }
     126        echo '</ul>';
     127
     128        if ( in_array( $post->post_status, [ 'draft', 'pending' ], true ) ) {
     129            $slug_restricted = [];
     130            $slug_reserved   = [];
     131
     132            // String length checks.
     133            if ( strlen( $slug ) < 5 ) {
     134                array_push( self::$flagged['med'], 'slug is less than 5 characters' );
     135            }
     136            if ( strlen( $slug ) > 50 ) {
     137                array_push( self::$flagged['med'], 'slug is more than 50 characters' );
     138            }
     139
     140            // Check if any term in the restricted/reserved is in the plugin slug.
     141            $slug_string = str_replace( '-', ' ', $slug );
     142
     143            foreach ( self::$restricted_slugs as $bad_slug ) {
     144                if ( false !== stristr( $slug_string, $bad_slug ) ) {
     145                    array_push( $slug_restricted, $bad_slug );
     146                }
     147            }
     148            foreach ( self::$reserved_slugs as $bad_slug ) {
     149                if ( false !== stristr( $slug_string, $bad_slug ) ) {
     150                    array_push( $slug_reserved, $bad_slug );
     151                }
     152            }
     153            if ( ! empty( $slug_restricted ) ) {
     154                array_push( self::$flagged['med'], 'plugin slug contains restricted term(s): ' . implode( ', ', $slug_restricted ) );
     155            }
     156            if ( ! empty( $slug_reserved ) ) {
     157                array_push( self::$flagged['high'], 'plugin slug contains reserved term(s): ' . implode( ', ', $slug_reserved ) );
     158            }
     159
     160            // Check slug usage.
     161            $plugin_api_usage = intval( get_post_meta( $post->ID, 'active_installs', true ) );
     162            if ( $plugin_api_usage >= '5000' ) {
     163                array_push( self::$flagged['high'], 'slug used by more than 5000 users' );
     164            } elseif ( $plugin_api_usage >= '1000' ) {
     165                array_push( self::$flagged['med'], 'slug used by 1000-5000 users' );
     166            } elseif ( $plugin_api_usage >= '500' ) {
     167                array_push( self::$flagged['low'], 'slug used by 500-1000 users' );
     168            }
     169
     170            // User account was registered less than 2 weeks ago (but longer than 3 days) (user is still fairly new).
     171            $two_weeks_ago  = time() - ( 2 * WEEK_IN_SECONDS );
     172            $three_days_ago = time() - ( 3 * DAY_IN_SECONDS );
     173            if ( strtotime( $author->user_registered ) > $two_weeks_ago && strtotime( $author->user_registered ) < $three_days_ago ) {
     174                array_push( self::$flagged['low'], 'account registered less than 2 weeks ago' );
     175            }
     176            if ( strtotime( $author->user_registered ) > $three_days_ago ) {
     177                array_push( self::$flagged['low'], 'account registered less than 3 days ago' );
     178            }
     179
     180            // Username ends in numbers.
     181            if ( is_numeric( substr( $author->user_login, - 1, 1 ) ) ) {
     182                array_push( self::$flagged['low'], 'username ends in numbers' );
     183            }
     184
     185            // User has no URL.
     186            if ( empty( $author->user_url ) ) {
     187                array_push( self::$flagged['low'], 'account has no URL' );
     188            }
     189
     190            foreach ( self::$weird_urls as $url ) {
     191                if ( false !== stripos( $author->user_url, $url ) ) {
     192                    array_push( self::$flagged['med'], 'account URL contains ' . $url );
     193                }
     194                if ( false !== stripos( $author->user_email, $url ) ) {
     195                    array_push( self::$flagged['med'], 'account email contains ' . $url );
     196                }
     197            }
     198
     199            // Reserved slugs are also often abused domain names (trademark law sucks).
     200            foreach ( self::$reserved_slugs as $url ) {
     201                if ( false !== stripos( $author->user_url, $url ) ) {
     202                    array_push( self::$flagged['high'], 'account URL contains ' . $url );
     203                }
     204                if ( false !== stripos( $author->user_email, $url ) ) {
     205                    array_push( self::$flagged['med'], 'account email contains ' . $url );
     206                }
     207            }
     208
     209            // User Behavior.
     210            // If FORUM ROLE is blocked.
     211            if ( defined( 'WPORG_SUPPORT_FORUMS_BLOGID' ) ) {
     212                $user = new \WP_User( $post->post_author, '', WPORG_SUPPORT_FORUMS_BLOGID );
     213                if ( ! empty( $user->allcaps['bbp_blocked'] ) ) {
     214                    array_push( self::$flagged['high'], 'user is blocked' );
     215                }
     216            }
     217
     218            // No plugins.
     219            if ( empty( $author_commit ) && empty( $author_plugins ) ) {
     220                array_push( self::$flagged['low'], 'user has no plugins' );
     221            }
     222
     223            // Echo flag results (everyone pretty much has at least one).
     224            echo '<ul class="plugin-flagged">';
     225            foreach ( self::$flagged as $flag => $reasons ) {
     226                if ( count( $reasons ) ) {
     227                    echo '<li class="plugin-flagged-' . esc_attr( $flag ) . '"><strong>' . esc_html( strtoupper( $flag ) ) . ' (' . esc_html( count( $reasons ) ) . '):</strong> ' . esc_html( implode( '; ', $reasons ) ) . '</li>';
     228                }
     229            }
     230            echo '</ul>';
     231        } else {
     232            ?>
     233            <ul>
     234                <li><a href='https://plugins.trac.wordpress.org/log/<?php echo esc_attr( $post->post_name ); ?>/'>Development Log</a></li>
     235                <li><a href='https://plugins.svn.wordpress.org/<?php echo esc_attr( $post->post_name ); ?>/'>Subversion Repository</a></li>
     236                <li><a href='https://plugins.trac.wordpress.org/browser/<?php echo esc_attr( $post->post_name ); ?>/'>Browse in Trac</a></li>
     237            </ul>
     238            <?php
     239        }
     240
     241        add_filter( 'wp_comment_reply', function ( $string ) use ( $post, $author ) {
    51242            $committers = Tools::get_plugin_committers( $post->post_name );
    52243            $committers = array_map( function ( $user_login ) {
     
    70261            ?>
    71262            <form id="contact-author" class="contact-author" method="POST" action="https://supportpress.wordpress.org/plugins/thread-new.php">
    72                 <input type="hidden" name="to_email" value="<?php echo esc_attr( $author->user_email ); ?>" />
    73                 <input type="hidden" name="to_name" value="<?php echo esc_attr( $author->display_name ); ?>" />
    74                 <input type="hidden" name="cc" value="<?php echo esc_attr( $cc_emails ); ?>" />
    75                 <input type="hidden" name="subject" value="<?php echo esc_attr( $subject ); ?>" />
    76                 <button class="button button-primary" type="submit"><?php _e( 'Contact plugin author', 'wporg-plugins' ); ?></button>
     263                <input type="hidden" name="to_email" value="<?php echo esc_attr( $author->user_email ); ?>"/>
     264                <input type="hidden" name="to_name" value="<?php echo esc_attr( $author->display_name ); ?>"/>
     265                <input type="hidden" name="cc" value="<?php echo esc_attr( $cc_emails ); ?>"/>
     266                <input type="hidden" name="subject" value="<?php echo esc_attr( $subject ); ?>"/>
     267                <button class="button button-primary" type="submit">Contact plugin author</button>
    77268            </form>
    78269            <?php
     
    82273    }
    83274}
    84 
Note: See TracChangeset for help on using the changeset viewer.