Plugin Directory

Changeset 3462863


Ignore:
Timestamp:
02/16/2026 07:51:00 PM (6 weeks ago)
Author:
_smartik_
Message:

Release 4.1.1 - Security hardening and new shortcode features

Location:
compact-archives
Files:
25 added
4 edited

Legend:

Unmodified
Added
Removed
  • compact-archives/trunk/changelog.md

    r2566779 r3462863  
     1= 4.1.1 =
     2* Improved input validation for shortcode parameters.
     3* Enhanced output sanitization.
     4* Added URL escaping for archive links.
     5* New: Added `classname` attribute to shortcode for custom CSS classes.
     6* New: Shortcode `before` attribute now accepts tag names without brackets (e.g., `before="div"`).
     7
     8= 4.1.0 =
     9* New: Support the block based Widget editor introduced in WordPress 5.8.
     10
     11= 4.0.0 =
     12* New: Improve caching of database queries by using the WordPress object cache.
     13* Enhancement: Use WordPress Core date internationalization.
     14* Enhancement: Use language files generated by translate.wordpress.org.
     15* Fix: Styling fixes for block editor archive block.
     16* Minimum version of WordPress updated to 4.7.
     17
    118= 3.0.9 =
    219* Update Readme.txt
  • compact-archives/trunk/compact.php

    r3113246 r3462863  
    44Plugin URI: http://www.wpbeginner.com
    55Description: Displays a compact monthly archive instead of the default long list. Either display it as a block suitable for the body of a page or in a form compact enough for a sidebar.
    6 Version: 4.1.0
     6Version: 4.1.1
    77Author: WPBeginner
    88Author URI: http://www.wpbeginner.com
     
    1010Requires PHP: 5.6
    1111Requires at least: 4.8
    12 Tested up to: 6.6
     12Tested up to: 6.9.1
    1313License: GPL-2.0+
    1414*/
  • compact-archives/trunk/inc/compact-archives.php

    r2566779 r3462863  
    101101
    102102    if ( empty( $dates ) ) {
    103         return $before . __( 'Archive is empty' ) . $after;
     103        return wp_kses_post( $before . __( 'Archive is empty' ) . $after );
    104104    }
    105105    $result = '';
    106106    foreach ( $dates as $year => $months ) {
    107         $result .= $before . '<strong><a href="' . get_year_link( $year ) . '">' . esc_html( $year ) . '</a>: </strong> ';
     107        $result .= $before . '<strong><a href="' . esc_url( get_year_link( $year ) ) . '">' . esc_html( $year ) . '</a>: </strong> ';
    108108        for ( $month = 1; $month <= 12; $month++ ) {
    109109            $month_has_posts = ( isset( $months[ $month ] ) );
     
    124124            }
    125125            if ( $month_has_posts ) {
    126                 $result .= '<a href="' . get_month_link( $year, $month ) . '" title="' . esc_attr( date_i18n( 'F Y', $dummydate ) ) . '">' . esc_html( $month_abbrev ) . '</a> ';
     126                $result .= '<a href="' . esc_url( get_month_link( $year, $month ) ) . '" title="' . esc_attr( date_i18n( 'F Y', $dummydate ) ) . '">' . esc_html( $month_abbrev ) . '</a> ';
    127127            } else {
    128128                $result .= '<span class="emptymonth">' . esc_html( $month_abbrev ) . '</span> ';
     
    154154    $atts = shortcode_atts(
    155155        array(
    156             'style'  => 'initial',
    157             'before' => '<li>',
    158             'after'  => '</li>',
     156            'style'     => 'initial',
     157            'before'    => '<li>',
     158            'after'     => '</li>',
     159            'classname' => '',
    159160        ),
    160161        $atts
    161162    );
    162163
    163     if ( '<li>' === $atts['before'] ) :
    164         $wrap = '<ul style="list-style-type: none; margin-top: 10px; margin-bottom 20px;">';
    165     endif;
    166 
    167     if ( '</li>' === $atts['after'] ) :
    168         $wrap_end = '</ul>';
    169     endif;
     164    // Validate style against allowlist.
     165    $allowed_styles = array( 'initial', 'block', 'numeric' );
     166    if ( ! in_array( $atts['style'], $allowed_styles, true ) ) {
     167        $atts['style'] = 'initial';
     168    }
     169
     170    // Allowed wrapper tags.
     171    $allowed_tags = array( 'li', 'p', 'div', 'span' );
     172
     173    // Normalize input: trim, lowercase, decode HTML entities, strip </p> from editor.
     174    $before_normalized = strtolower( trim( html_entity_decode( $atts['before'] ) ) );
     175    $before_normalized = trim( str_ireplace( '</p>', '', $before_normalized ) );
     176
     177    // Extract tag name (strip < > / to support both "div" and "<div>").
     178    $tag_name = preg_replace( '/[<>\/]/', '', $before_normalized );
     179
     180    if ( ! in_array( $tag_name, $allowed_tags, true ) ) {
     181        $tag_name = 'li';
     182    }
     183
     184    // Sanitize classname: split by space, sanitize each, rejoin.
     185    $class_attr = '';
     186    if ( ! empty( $atts['classname'] ) ) {
     187        $classes = preg_split( '/\s+/', trim( $atts['classname'] ) );
     188        $sanitized_classes = array_map( 'sanitize_html_class', $classes );
     189        $sanitized_classes = array_filter( $sanitized_classes ); // Remove empty.
     190        if ( ! empty( $sanitized_classes ) ) {
     191            $class_attr = ' class="' . esc_attr( implode( ' ', $sanitized_classes ) ) . '"';
     192        }
     193    }
     194
     195    $wrap     = '';
     196    $wrap_end = '';
     197
     198    if ( 'li' === $tag_name ) {
     199        // For li, apply classname to the ul container.
     200        $atts['before'] = '<' . $tag_name . '>';
     201        $wrap           = '<ul' . $class_attr . ' style="list-style-type: none; margin-top: 10px; margin-bottom: 20px;">';
     202        $wrap_end       = '</ul>';
     203    } else {
     204        // For other tags, apply classname to the before tag.
     205        $atts['before'] = '<' . $tag_name . $class_attr . '>';
     206    }
     207
     208    $atts['after'] = '</' . $tag_name . '>';
    170209
    171210    return $wrap . get_compact_archive( $atts['style'], $atts['before'], $atts['after'] ) . $wrap_end;
     
    311350    }
    312351
     352    // Validate archive type against allowlist.
     353    $allowed_types = array( 'initial', 'block', 'numeric' );
     354    if ( ! in_array( $attributes['compact_archive_type'], $allowed_types, true ) ) {
     355        $attributes['compact_archive_type'] = 'block';
     356    }
     357
     358    // Validate text case against allowlist.
     359    $allowed_text_cases = array( 'none', 'capitalize', 'uppercase' );
     360    if ( ! in_array( $attributes['compact_archive_text_case'], $allowed_text_cases, true ) ) {
     361        $attributes['compact_archive_text_case'] = 'none';
     362    }
     363
    313364    // Default styles.
    314365    $style = ( ! empty( $attributes['compact_archive_title'] ) ) ? 'list-style-type: none; margin-top: 10px; margin-bottom 20px;' : 'list-style-type: none; margin-top: 20px; margin-bottom 20px;';
     
    321372    }
    322373
    323     return ( ! empty( $attributes['compact_archive_title'] ) ? '<p>' . wp_kses_post( $attributes['compact_archive_title'] ) . '</p>' : '' ) . '<ul style="' . esc_attr( $style ) . '">' . get_compact_archive( $attributes['compact_archive_type'] ) . '</ul>';
    324 }
     374    // Sanitize title with restrictive allowlist matching RichText formatting controls.
     375    $title_html = '';
     376    if ( ! empty( $attributes['compact_archive_title'] ) ) {
     377        $allowed_title_html = array(
     378            'strong' => array(),
     379            'b'      => array(),
     380            'em'     => array(),
     381            'i'      => array(),
     382            'a'      => array(
     383                'href'  => true,
     384                'title' => true,
     385                'rel'   => true,
     386            ),
     387        );
     388        $title_html = '<p>' . wp_kses( $attributes['compact_archive_title'], $allowed_title_html ) . '</p>';
     389    }
     390
     391    return $title_html . '<ul style="' . esc_attr( $style ) . '">' . get_compact_archive( $attributes['compact_archive_type'] ) . '</ul>';
     392}
  • compact-archives/trunk/readme.txt

    r3113244 r3462863  
    11=== Compact Archives ===
    2 Contributors: smub, noumaan, deb255
    3 Tags: archive, archives, yearly archive, montly archive, yearly, monthly, annually, archive by month, archive by year
    4 Tested up to: 6.6
    5 Stable tag: 4.1.0
     2Contributors: smub, noumaan, _smartik_
     3Tags: archive, archives, monthly archive, yearly archive, widget
     4Tested up to: 6.9.1
     5Stable tag: 4.1.1
    66Requires PHP: 5.6
    77Requires at least: 4.8
     
    104104= How do I get different layouts using shortcode? =
    105105
    106 The shortcode `[compact_archive]` works just like the template tag. It accepts three parameters which are style, before, and after.
     106The shortcode `[compact_archive]` works just like the template tag. It accepts the following parameters:
    107107
    108 Using shortcode `[compact_archive style="block"]` will display compact archives in block.
     108* `style` - Display format: `initial` (default), `block`, or `numeric`
     109* `before` - Wrapper tag: `li` (default), `p`, `div`, or `span`. Can be written with or without angle brackets.
     110* `classname` - Custom CSS class(es) to add to the container
    109111
    110 Using shortcode `[compact_archive style="numeric" before="<p>" after="</p>"]` will display compact archive in numeric form, wrapped in a paragraph tag.
     112Examples:
     113
     114`[compact_archive style="block"]` - Display archives in block format.
     115
     116`[compact_archive style="numeric" before="p"]` - Display in numeric form, wrapped in paragraph tags.
     117
     118`[compact_archive before="div" classname="my-archive custom-class"]` - Wrap each line in a div with custom CSS classes.
    111119
    112120= How do I get different layouts using Compact Archive Widget in Sidebar? =
     
    117125
    118126The year links at the start of each line are wrapped in <strong></strong> tags while months with no posts are wrapped with <span class="emptymonth"></span> so you can differentiate them visually using your style sheet.
     127
     128You can also use the `classname` attribute in the shortcode to add custom CSS classes:
     129
     130`[compact_archive classname="my-custom-archive"]`
    119131
    120132= What if My Site is in Another Language? =
     
    130142
    131143== Changelog ==
     144
     145= 4.1.1 =
     146* Improved input validation for shortcode parameters.
     147* Enhanced output sanitization.
     148* Added URL escaping for archive links.
     149* New: Added `classname` attribute to shortcode for custom CSS classes.
     150* New: Shortcode `before` attribute now accepts tag names without brackets (e.g., `before="div"`).
    132151
    133152= 4.1.0 =
     
    145164== Upgrade Notice ==
    146165
     166= 4.1.1 =
     167
     168Maintenance release with security hardening. Update recommended.
     169
    147170= 4.1.0 =
    148171
Note: See TracChangeset for help on using the changeset viewer.