WordPress.org

Making WordPress.org


Ignore:
Timestamp:
10/13/17 08:56:56 (2 months ago)
Author:
dd32
Message:

Plugin Directory: Generate md5 hashes for plugins.

This is a POC and may change or be removed in the future, it's here for testing purposes.

A api.wordpress.org endpoint may be available in the future to access it.
This is only enabled for the 'exploit-scanner' plugin at present, purely for testing, as it publishes the md5 hashes of its own files already

Compare https://wordpress.org/plugins/exploit-scanner/ to https://downloads.wordpress.org/plugins/exploit-scanner.1.5.2.checksums.json

See #3192

Location:
sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/zip
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/zip/class-builder.php

    r5246 r6022  
    1515    const ZIP_SVN_URL = PLUGIN_ZIP_SVN_URL; 
    1616 
    17     protected $zip_file = ''; 
    18     protected $tmp_build_dir  = ''; 
    19     protected $tmp_dir = ''; 
    20  
    21     protected $slug    = ''; 
    22     protected $version = ''; 
    23     protected $context = ''; 
    24  
    25     /** 
    26      * Generate a ZIP for a provided Plugin versions. 
     17    protected $zip_file      = ''; 
     18    protected $checksum_file = ''; 
     19    protected $tmp_build_dir = ''; 
     20    protected $tmp_dir       = ''; 
     21 
     22    protected $slug       = ''; 
     23    protected $version    = ''; 
     24    protected $context    = ''; 
     25    protected $stable_tag = ''; 
     26 
     27    /** 
     28     * Generate a ZIP for a provided Plugin tags. 
    2729     * 
    2830     * @param string $slug     The plugin slug. 
     
    3032     * @param string $context  The context of this Builder instance (commit #, etc) 
    3133     */ 
    32     public function build( $slug, $versions, $context = '' ) { 
     34    public function build( $slug, $versions, $context = '', $stable_tag = '' ) { 
    3335        // Bail when in an unconfigured environment. 
    3436        if ( ! defined( 'PLUGIN_ZIP_SVN_URL' ) ) { 
     
    3638        } 
    3739 
    38         $this->slug     = $slug; 
    39         $this->versions = $versions; 
    40         $this->context  = $context; 
     40        $this->slug       = $slug; 
     41        $this->versions   = $versions; 
     42        $this->context    = $context; 
     43        $this->stable_tag = $stable_tag; 
    4144 
    4245        // General TMP directory 
     
    97100            // Pull the ZIP file down we're going to modify, which may not already exist. 
    98101            SVN::up( $this->zip_file ); 
     102            // This is done within the checksum generation function due to us not knowing the checksum filename until export_plugin(). 
     103            // SVN::up( $this->checksum_file ); 
    99104 
    100105            try { 
     
    104109 
    105110                $this->export_plugin(); 
    106                 $this->fix_directory_dates();            
     111                $this->fix_directory_dates(); 
     112 
    107113                $this->generate_zip(); 
     114 
     115                $this->generate_checksums(); 
     116 
    108117                $this->cleanup_plugin_tmp(); 
    109118 
     
    114123                // Perform an SVN up to revert any changes made. 
    115124                SVN::up( $this->zip_file ); 
     125                if ( $this->checksum_file ) { 
     126                    SVN::up( $this->checksum_file ); 
     127                } 
    116128                continue; 
    117129            } 
    118130 
    119131            // Add the ZIP file to SVN - This is only really needed for new files which don't exist in SVN. 
    120             SVN::add( $this->zip_file );             
     132            SVN::add( $this->zip_file ); 
     133            if ( $this->checksum_file ) { 
     134                SVN::add( $this->checksum_file ); 
     135            } 
    121136        } 
    122137 
     
    146161 
    147162    /** 
     163     * Generates a JSON file containing the checksums of the files within the ZIP. 
     164     * 
     165     * In the event that a previous ZIP for this version exists, checksums for all versions of the file will be included. 
     166     */ 
     167    function generate_checksums() { 
     168        // Only enable this for the `exploit-scanner` plugin for the time being. 
     169        if ( 'exploit-scanner' != $this->slug ) { 
     170            return; 
     171        } 
     172 
     173        // Don't create checksums for trunk. 
     174        if ( ! $this->stable_tag || ( 'trunk' == $this->version && 'trunk' != $this->stable_tag && '' != $this->stable_tag ) ) { 
     175            return; 
     176        } 
     177 
     178        // Fetch the plugin headers 
     179        $plugin_data = false; 
     180        foreach ( glob( $this->tmp_build_dir . '/' . $this->slug . '/*.php' ) as $filename ) { 
     181            $plugin_data = get_plugin_data( $filename, false, false ); 
     182 
     183            if ( $plugin_data['Name'] && '' !== $plugin_data['Version'] ) { 
     184                break; 
     185            } 
     186        } 
     187 
     188        if ( ! $plugin_data || '' === $plugin_data['Version'] ) { 
     189            return; 
     190        } 
     191 
     192        $plugin_version = $plugin_data['Version']; 
     193        // Catch malformed version strings. 
     194        if ( basename( $plugin_version ) != $plugin_version ) { 
     195            return; 
     196        } 
     197 
     198        $this->checksum_file = "{$this->tmp_dir}/{$this->slug}/{$this->slug}.{$plugin_version}.checksums.json"; 
     199 
     200        // Checkout the Checksum file for this plugin version 
     201        SVN::up( $this->checksum_file ); 
     202 
     203        // Existing checksums? 
     204        $existing_json_checksum_file = file_exists( $this->checksum_file ); 
     205 
     206        $this->exec( sprintf( 
     207            'cd %s && find . -type f -print0 | sort -z | xargs -0 md5sum 2>&1', 
     208            escapeshellarg( $this->tmp_build_dir . '/' . $this->slug ) 
     209        ), $checksum_output, $return_value ); 
     210 
     211        if ( $return_value ) { 
     212        //  throw new Exception( __METHOD__ . ': Checksum generation failed, return code: ' . $return_value, 503 ); 
     213        // For now, just silently bail. 
     214            return; 
     215        } 
     216 
     217        $checksums = array(); 
     218        foreach ( $checksum_output as $line ) { 
     219            list( $md5, $filename ) = preg_split( '!\s+!', $line ); 
     220            $filename = preg_replace( '!^./!', '', $filename ); 
     221            $checksums[ trim( $filename ) ] = trim( $md5 ); 
     222        } 
     223 
     224        $json_checksum_file = (object) array( 
     225            'plugin'     => $this->slug, 
     226            'version'    => $plugin_version, 
     227            'source_tag' => $this->version, 
     228            'zip'        => basename( $this->zip_file ), 
     229            'checksums'  => $checksums 
     230        ); 
     231 
     232        // If the checksum file exists already, merge it into this one. 
     233        if ( $existing_json_checksum_file ) { 
     234            $existing_json_checksum_file = json_decode( file_get_contents( $this->checksum_file ) ); 
     235 
     236            if ( $existing_json_checksum_file && ! empty( $existing_json_checksum_file->checksums ) ) { 
     237                foreach ( $existing_json_checksum_file->checksums as $file => $checksum_details ) { 
     238 
     239                    if ( ! isset( $json_checksum_file->checksums[ $file ] ) ) { 
     240                        // Deleted file, include it in checksums. 
     241                        $json_checksum_file->checksums[ $file ] = $checksum_details; 
     242 
     243                    } elseif ( $json_checksum_file->checksums[ $file ] != $checksum_details ) { 
     244                        // Checksum has changed, include both in the resulting json file. 
     245                        if ( is_array( $checksum_details ) ) { 
     246                            $checksum_details[] = $json_checksum_file->checksums[ $file ]; 
     247                            $json_checksum_file->checksums[ $file ] = $checksum_details; 
     248                        } else { 
     249                            $json_checksum_file->checksums[ $file ] = array( 
     250                                $json_checksum_file->checksums[ $file ], 
     251                                $checksum_details 
     252                            ); 
     253                        } 
     254                    } 
     255                } 
     256            } 
     257        } 
     258 
     259        file_put_contents( $this->checksum_file, wp_json_encode( $json_checksum_file ) ); 
     260    } 
     261 
     262    /** 
    148263     * Generates a temporary unique directory in a given directory 
    149264     * 
     
    279394     */ 
    280395    public function invalidate_zip_caches( $versions ) { 
    281         // TODO: Implement PURGE  
     396        // TODO: Implement PURGE 
    282397        return true; 
    283398        if ( ! defined( 'PLUGIN_ZIP_X_ACCEL_REDIRECT_LOCATION' ) ) { 
  • sites/trunk/wordpress.org/public_html/wp-content/plugins/plugin-directory/zip/class-serve.php

    r5305 r6022  
    3737        $zip = basename( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ) ); 
    3838 
    39         $slug = false; 
     39        if ( ! preg_match( "!^(?P<slug>[a-z0-9-_]+)(\.(?P<version>.+?))?\.(?P<request_type>zip|checksums\.json)$!i", $zip, $m ) ) { 
     40            throw new Exception( __METHOD__ . ": Invalid URL." ); 
     41        } 
     42 
     43        $slug = strtolower( $m['slug'] ); 
     44 
    4045        $version = 'trunk'; 
    41  
    42         if ( ! preg_match( "!^(?P<slug>[a-z0-9-_]+)(.(?P<version>.+))?.zip$!i", $zip, $m ) ) { 
    43             throw new Exception( __METHOD__ . ": Invalid URL." ); 
    44         } 
    45  
    46         $slug = strtolower( $m['slug'] ); 
    47         if ( isset( $m['version'] ) ) { 
     46        if ( isset( $m['version'] ) && '' !== $m['version'] ) { 
    4847            $version = $m['version']; 
    4948        } 
    50  
    5149        if ( 'latest-stable' == $version ) { 
    5250            $version = $this->get_stable_tag( $slug ); 
     51        } 
     52 
     53        if ( 'zip' == strtolower( $m['request_type'] ) ) { 
     54            $checksum_request = false; 
     55        } else { 
     56            $checksum_request = true; 
     57 
     58            // Checksum requests for 'trunk' are not possible. 
     59            if ( 'trunk' == $version ) { 
     60                throw new Exception( __METHOD__ . ": Checksum requests must include a version." ); 
     61            } 
     62 
    5363        } 
    5464 
     
    5666            'stats' => true, 
    5767        ); 
    58         if ( isset( $_GET['stats'] ) ) { 
     68 
     69        if ( $checksum_request ) { 
     70            $args['stats'] = false; 
     71 
     72        } elseif ( isset( $_GET['stats'] ) ) { 
    5973            $args['stats'] = (bool) $_GET['stats']; 
     74 
    6075        } elseif ( isset( $_GET['nostats'] ) ) { 
    6176            $args['stats'] = !empty( $_GET['nostats'] ); 
    6277        } 
    6378 
    64         return compact( 'zip', 'slug', 'version', 'args' ); 
     79        return compact( 'zip', 'slug', 'version', 'args', 'checksum_request' ); 
    6580    } 
    6681 
     
    118133 
    119134    /** 
    120      * Returns the files to use for the request. 
     135     * Returns the file to be served for the request. 
    121136     * 
    122137     * @param array $request The request object for the request. 
    123      * @return array An array containing the files to use for the request, 'zip' and 'md5'. 
     138     * @return array The file to serve. 
    124139     */ 
    125140    protected function get_file( $request ) { 
    126         if ( empty( $request['version'] ) || 'trunk' == $request['version'] ) { 
     141        // Checksum requests must include a version 
     142        if ( $request['checksum_request'] ) { 
     143            return "{$request['slug']}/{$request['slug']}.{$request['version']}.checksums.json"; 
     144 
     145        } elseif ( empty( $request['version'] ) || 'trunk' == $request['version'] ) { 
    127146            return "{$request['slug']}/{$request['slug']}.zip"; 
     147 
    128148        } else { 
    129149            return "{$request['slug']}/{$request['slug']}.{$request['version']}.zip"; 
    130150        } 
     151 
    131152    } 
    132153 
     
    137158     */ 
    138159    protected function serve_zip( $request ) { 
    139         $zip = $this->get_file( $request ); 
     160        $file = $this->get_file( $request ); 
    140161 
    141162        if ( defined( 'PLUGIN_ZIP_X_ACCEL_REDIRECT_LOCATION' ) ) { 
    142             $zip_url = PLUGIN_ZIP_X_ACCEL_REDIRECT_LOCATION . $zip; 
    143  
    144             header( 'Content-Type: application/zip' ); 
    145             header( 'Content-Disposition: attachment; filename=' . basename( $zip ) ); 
    146             header( "X-Accel-Redirect: $zip_url" ); 
     163            $file_url = PLUGIN_ZIP_X_ACCEL_REDIRECT_LOCATION . $file; 
     164 
     165            if ( $request['checksum_request'] ) { 
     166                header( 'Content-Type: application/json' ); 
     167            } else { 
     168                header( 'Content-Type: application/zip' ); 
     169                header( 'Content-Disposition: attachment; filename=' . basename( $file ) ); 
     170            } 
     171            header( "X-Accel-Redirect: $file_url" ); 
    147172        } else { 
    148173            header( 'Content-Type: text/plain' ); 
    149             echo "This is a request for $zip, this server isn't currently configured to serve zip files.\n"; 
     174            echo "This is a request for $file, this server isn't currently configured to serve files.\n"; 
    150175        } 
    151176 
Note: See TracChangeset for help on using the changeset viewer.