Ignore:
Timestamp:
06/04/09 00:08:33 (3 years ago)
Author:
KennethLavrsen
Message:

Item1640: CommentPlugin writes "%" as html-code, which prevents the use of Macros
It was me that created the problem with my default safe mode in URLPARAM
I have analysed the problem and as long as we use URLPARAM in the OUTPUT part of
the CommentPlugin templates and keep the settings inside verbatim tags we do not
expose any XSS attack. So we can give the users back the ability to use Foswiki
Macros in comment input fields.
With this I also merge over some code changes Crawford had done in trunk.
Note that except for the release version all changes in the .pm files are unrelated
to the bug fix, which is why I dare checking in perltidy stuff with a bug fix.
CommentPlugin is now again same in trunk and Release branch

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/Release01x00/CommentPlugin/lib/Foswiki/Plugins/CommentPlugin/Comment.pm

    r1984 r4026  
    3737# PUBLIC save the given comment. 
    3838sub save { 
     39 
    3940    #my ( $text, $topic, $web ) = @_; 
    4041 
    4142    my $wikiName = Foswiki::Func::getWikiName(); 
    42     if( ! Foswiki::Func::checkAccessPermission( 'change', $wikiName, '', 
    43                                                                                           $_[1], $_[2] ) ) { 
     43    if ( 
     44        !Foswiki::Func::checkAccessPermission( 
     45            'change', $wikiName, '', $_[1], $_[2] 
     46        ) 
     47      ) 
     48    { 
     49 
    4450        # user has no permission to change the topic 
    45         throw Foswiki::OopsException( 'accessdenied', 
    46                                     def => 'topic_access', 
    47                                     web => $_[2], 
    48                                     topic => $_[1] ); 
    49     } else { 
    50         _buildNewTopic( @_ ); 
     51        throw Foswiki::OopsException( 
     52            'accessdenied', 
     53            def   => 'topic_access', 
     54            web   => $_[2], 
     55            topic => $_[1] 
     56        ); 
     57    } 
     58    else { 
     59        _buildNewTopic(@_); 
    5160    } 
    5261} 
     
    5463# PUBLIC STATIC convert COMMENT statements to form prompts 
    5564sub prompt { 
     65 
    5666    #my ( $previewing, $text, $web, $topic ) = @_; 
    5767 
    5868    my $defaultType = 
    5969      Foswiki::Func::getPreferencesValue('COMMENTPLUGIN_DEFAULT_TYPE') 
    60           || 'above'; 
     70      || 'above'; 
    6171 
    6272    my $message = ''; 
     73 
    6374    # Is commenting disabled? 
    6475    my $disable = ''; 
    6576    if ( $_[0] ) { 
     77 
    6678        # We are in Preview mode 
    67         $message  = "(Edit - Preview)"; 
     79        $message = "(Edit - Preview)"; 
    6880        $disable = 'disabled'; 
    6981    } 
    7082 
    7183    my $idx = 0; 
    72     $_[1] =~ s/%COMMENT({.*?})?%/_handleInput($1,$_[2],$_[3],\$idx,$message,$disable,$defaultType)/eg; 
     84    $_[1] =~ 
     85s/%COMMENT({.*?})?%/_handleInput($1,$_[2],$_[3],\$idx,$message,$disable,$defaultType)/eg; 
    7386} 
    7487 
     
    8497    my $templatetopic = ''; 
    8598    my $templateweb = $web || ''; 
    86     if ( $attrtemplatetopic ) { 
    87         my ($templocweb, $temploctopic ) = 
    88           Foswiki::Func::normalizeWebTopicName($templateweb, $attrtemplatetopic); 
     99    if ($attrtemplatetopic) { 
     100        my ( $templocweb, $temploctopic ) = 
     101          Foswiki::Func::normalizeWebTopicName( $templateweb, 
     102            $attrtemplatetopic ); 
    89103        $templatetopic = "$templocweb.$temploctopic"; 
    90104    } 
     
    94108# PRIVATE generate an input form for a %COMMENT tag 
    95109sub _handleInput { 
    96     my ( $attributes, $web, $topic, $pidx, $message, 
    97          $disable, $defaultType ) = @_; 
    98  
    99     $attributes =~ s/^{(.*)}$/$1/ if ( $attributes ); 
     110    my ( $attributes, $web, $topic, $pidx, $message, $disable, $defaultType ) = 
     111      @_; 
     112 
     113    $attributes =~ s/^{(.*)}$/$1/ if ($attributes); 
    100114 
    101115    my $attrs = new Foswiki::Attrs( $attributes, 1 ); 
    102     my $type = 
    103       $attrs->remove( 'type' ) || $attrs->remove( 'mode' ) || $defaultType; 
    104     my $silent = $attrs->remove( 'nonotify' ); 
    105     my $location = $attrs->remove( 'location' ); 
    106     my $remove = $attrs->remove( 'remove' ); 
    107     my $nopost = $attrs->remove( 'nopost' ); 
    108     my $default = $attrs->remove( 'default' ); 
    109     my $attrtemplatetopic = $attrs->remove( 'templatetopic' ) || ''; 
    110     my $templatetopic = _getTemplateLocation( $attrtemplatetopic, $web ); 
     116    my $type = $attrs->remove('type') || $attrs->remove('mode') || $defaultType; 
     117    my $silent            = $attrs->remove('nonotify'); 
     118    my $location          = $attrs->remove('location'); 
     119    my $remove            = $attrs->remove('remove'); 
     120    my $nopost            = $attrs->remove('nopost'); 
     121    my $default           = $attrs->remove('default'); 
     122    my $attrtemplatetopic = $attrs->remove('templatetopic') || ''; 
     123    my $templatetopic     = _getTemplateLocation( $attrtemplatetopic, $web ); 
    111124 
    112125    $message ||= $default || ''; 
    113126    $message ||= $default || ''; 
    114         $disable ||= ''; 
     127    $disable ||= ''; 
    115128 
    116129    # clean off whitespace 
     
    120133    # Expand the template in the context of the web where the comment 
    121134    # box is (not the target of the comment!) 
    122     my $input = _getTemplate( "PROMPT:$type", $web, $topic, $templatetopic ) || ''; 
     135    my $input = _getTemplate( "PROMPT:$type", $web, $topic, $templatetopic ) 
     136      || ''; 
    123137    return $input if $input =~ m/^%RED%/so; 
    124138 
     
    129143    # change the url if it is. 
    130144    my $anchor = undef; 
    131     my $target = $attrs->remove( 'target' ); 
    132     if ( $target ) { 
     145    my $target = $attrs->remove('target'); 
     146    if ($target) { 
     147 
    133148        # extract web and anchor 
    134149        if ( $target =~ s/^(\w+)\.// ) { 
     
    156171        if ( $disable eq '' ) { 
    157172            my $hiddenFields = ""; 
    158             $hiddenFields .= "\n".CGI::hidden( 
    159                 -name=>'comment_action', -value=>'save' ); 
    160             $hiddenFields .= "\n".CGI::hidden( 
    161                 -name=>'comment_type', -value=>$type ); 
    162             if( defined( $silent )) { 
    163                 $hiddenFields .= "\n".CGI::hidden( 
    164                     -name=>'comment_nonotify', value=>1 ); 
    165             } 
    166             if ( $templatetopic ) { 
    167                 $hiddenFields .= "\n".CGI::hidden( 
    168                     -name=>'comment_templatetopic', -value=>$templatetopic ); 
    169             } 
    170             if ( $location ) { 
    171                 $hiddenFields .= "\n".CGI::hidden( 
    172                     -name=>'comment_location', -value=>$location ); 
    173             } elsif ( $anchor ) { 
    174                 $hiddenFields .= "\n".CGI::hidden( 
    175                     -name=>'comment_anchor', -value=>$anchor ); 
    176             } else { 
    177                 $hiddenFields .= "\n".CGI::hidden( 
    178                     -name=>'comment_index', -value=>$$pidx ); 
    179             } 
    180             if( $nopost ) { 
    181                 $hiddenFields .= "\n".CGI::hidden( 
    182                     -name=>'comment_nopost', -value=>$nopost ); 
    183             } 
    184             if( $remove ) { 
    185                 $hiddenFields .= "\n".CGI::hidden( 
    186                     -name=>'comment_remove', -value=>$$pidx ); 
     173            $hiddenFields .= 
     174              "\n" . CGI::hidden( -name => 'comment_action', -value => 'save' ); 
     175            $hiddenFields .= 
     176              "\n" . CGI::hidden( -name => 'comment_type', -value => $type ); 
     177            if ( defined($silent) ) { 
     178                $hiddenFields .= 
     179                  "\n" . CGI::hidden( -name => 'comment_nonotify', value => 1 ); 
     180            } 
     181            if ($templatetopic) { 
     182                $hiddenFields .= "\n" 
     183                  . CGI::hidden( 
     184                    -name  => 'comment_templatetopic', 
     185                    -value => $templatetopic 
     186                  ); 
     187            } 
     188            if ($location) { 
     189                $hiddenFields .= "\n" 
     190                  . CGI::hidden( 
     191                    -name  => 'comment_location', 
     192                    -value => $location 
     193                  ); 
     194            } 
     195            elsif ($anchor) { 
     196                $hiddenFields .= "\n" 
     197                  . CGI::hidden( -name => 'comment_anchor', -value => $anchor ); 
     198            } 
     199            else { 
     200                $hiddenFields .= "\n" 
     201                  . CGI::hidden( -name => 'comment_index', -value => $$pidx ); 
     202            } 
     203            if ($nopost) { 
     204                $hiddenFields .= "\n" 
     205                  . CGI::hidden( -name => 'comment_nopost', -value => $nopost ); 
     206            } 
     207            if ($remove) { 
     208                $hiddenFields .= "\n" 
     209                  . CGI::hidden( -name => 'comment_remove', -value => $$pidx ); 
    187210            } 
    188211            $input .= $hiddenFields; 
    189212        } 
    190         if ( $noform ) { 
    191             my $form = _getTemplate( "FORM:$type", $topic, $web, 
    192                                      $templatetopic, 'off' ) || ''; 
    193             if ( $form ) { 
     213        if ($noform) { 
     214            my $form = 
     215              _getTemplate( "FORM:$type", $topic, $web, $templatetopic, 'off' ) 
     216              || ''; 
     217            if ($form) { 
    194218                $form =~ s/%COMMENTPROMPT%/$input/; 
    195219                $input = $form; 
    196220            } 
    197221        } 
    198         unless ($noform eq 'on') { 
    199             $input = CGI::start_form( -name => $type.$n, 
    200                                       -id => $type.$n, 
    201                                       -action=>$url, 
    202                                       -method=>'post' ).$input.CGI::end_form(); 
     222        unless ( $noform eq 'on' ) { 
     223            $input = CGI::start_form( 
     224                -name   => $type . $n, 
     225                -id     => $type . $n, 
     226                -action => $url, 
     227                -method => 'post' 
     228              ) 
     229              . $input 
     230              . CGI::end_form(); 
    203231        } 
    204232    } 
     
    214242 
    215243    # Get the templates. 
    216     my $templateFile = $templatetopic 
    217         || Foswiki::Func::getPreferencesValue('COMMENTPLUGIN_TEMPLATES') 
    218           || 'comments'; 
    219  
    220     my $templates = 
    221       Foswiki::Func::loadTemplate( $templateFile ); 
    222     if (! $templates ) { 
    223         Foswiki::Func::writeWarning("Could not read template file '$templateFile'"); 
     244    my $templateFile = 
     245         $templatetopic 
     246      || Foswiki::Func::getPreferencesValue('COMMENTPLUGIN_TEMPLATES') 
     247      || 'comments'; 
     248 
     249    my $templates = Foswiki::Func::loadTemplate($templateFile); 
     250    if ( !$templates ) { 
     251        Foswiki::Func::writeWarning( 
     252            "Could not read template file '$templateFile'"); 
    224253        return; 
    225254    } 
    226255 
    227     my $t = Foswiki::Func::expandTemplate( $name ); 
     256    my $t = Foswiki::Func::expandTemplate($name); 
    228257    return "%RED%No such template def TMPL:DEF{$name}%ENDCOLOR%" 
    229258      unless ( defined($t) && $t ne '' ) || $warn eq 'off'; 
     
    237266 
    238267    my $val = $attrs->{$name}; 
    239     return $val if defined( $val ); 
     268    return $val if defined($val); 
    240269    return $default; 
    241270} 
     
    243272# PRIVATE STATIC Performs comment insertion in the topic. 
    244273sub _buildNewTopic { 
     274 
    245275    #my ( $text, $topic, $web ) = @_; 
    246276    my ( $topic, $web ) = ( $_[1], $_[2] ); 
     
    249279    return unless $query; 
    250280 
    251     my $type = $query->param( 'comment_type' ) || 
    252       Foswiki::Func::getPreferencesValue('COMMENTPLUGIN_DEFAULT_TYPE') || 
    253           'above'; 
    254     my $index = $query->param( 'comment_index' ) || 0; 
    255     my $anchor = $query->param( 'comment_anchor' ); 
    256     my $location = $query->param( 'comment_location' ); 
    257     my $remove = $query->param( 'comment_remove' ); 
    258     my $nopost = $query->param( 'comment_nopost' ); 
    259     my $templatetopic = $query->param( 'comment_templatetopic' ) || ''; 
     281    my $type = 
     282         $query->param('comment_type') 
     283      || Foswiki::Func::getPreferencesValue('COMMENTPLUGIN_DEFAULT_TYPE') 
     284      || 'above'; 
     285    my $index         = $query->param('comment_index') || 0; 
     286    my $anchor        = $query->param('comment_anchor'); 
     287    my $location      = $query->param('comment_location'); 
     288    my $remove        = $query->param('comment_remove'); 
     289    my $nopost        = $query->param('comment_nopost'); 
     290    my $templatetopic = $query->param('comment_templatetopic') || ''; 
    260291 
    261292    my $output = _getTemplate( "OUTPUT:$type", $topic, $web, $templatetopic ); 
     
    266297    # Expand the template 
    267298    my $position = 'AFTER'; 
    268     if( $output =~ s/%POS:(.*?)%//g ) { 
     299    if ( $output =~ s/%POS:(.*?)%//g ) { 
    269300        $position = $1; 
    270301    } 
     
    285316    # text if the =text= parameter isn't specified - which for comments, 
    286317    # it isn't. 
    287     my $premeta = ''; 
     318    my $premeta  = ''; 
    288319    my $postmeta = ''; 
    289     my $inpost = 0; 
    290     my $text = ''; 
    291     foreach my $line ( split( /\r?\n/, $_[0] )) { 
    292         if( $line =~ /^%META:[A-Z]+{[^}]*}%/ ) { 
    293             if ( $inpost) { 
    294                 $postmeta .= $line."\n"; 
    295             } else { 
    296                 $premeta .= $line."\n"; 
    297             } 
    298         } else { 
    299             $text .= $line."\n"; 
     320    my $inpost   = 0; 
     321    my $text     = ''; 
     322    foreach my $line ( split( /\r?\n/, $_[0] ) ) { 
     323        if ( $line =~ /^%META:[A-Z]+{[^}]*}%/ ) { 
     324            if ($inpost) { 
     325                $postmeta .= $line . "\n"; 
     326            } 
     327            else { 
     328                $premeta .= $line . "\n"; 
     329            } 
     330        } 
     331        else { 
     332            $text .= $line . "\n"; 
    300333            $inpost = 1; 
    301334        } 
    302335    } 
     336 
    303337    #make sure the anchor or location exits 
    304     if (defined($location) and not($text =~ /(?<!location\=\")($location)/)) { 
     338    if ( defined($location) and not( $text =~ /(?<!location\=\")($location)/ ) ) 
     339    { 
    305340        undef $location; 
    306341    } 
    307     if (defined($anchor) and not($text =~ /^($anchor\s)/)) { 
     342    if ( defined($anchor) and not( $text =~ /^($anchor\s)/ ) ) { 
    308343        undef $anchor; 
    309344    } 
    310345 
    311     unless( $nopost ) { 
    312         if( $position eq 'TOP' ) { 
    313             $text = $output.$text; 
    314         } elsif ( $position eq 'BOTTOM' ) { 
     346    unless ($nopost) { 
     347        if ( $position eq 'TOP' ) { 
     348            $text = $output . $text; 
     349        } 
     350        elsif ( $position eq 'BOTTOM' ) { 
     351 
    315352            # Awkward newlines here, to avoid running into meta-data. 
    316353            # This should _not_ be a problem. 
     
    318355            $text .= "\n" unless $output =~ m/^\n/s; 
    319356            $text .= $output; 
    320             $text .= "\n" unless $text =~ m/\n$/s; 
    321         } else { 
    322             if ( $location ) { 
     357            $text .= "\n" unless $text   =~ m/\n$/s; 
     358        } 
     359        else { 
     360            if ($location) { 
    323361                if ( $position eq 'BEFORE' ) { 
    324                     $text =~ s/(?<!location\=\")($location)/$output$1/m; 
    325                 } else { # AFTER 
    326                     $text =~ s/(?<!location\=\")($location)/$1$output/m; 
     362                    $text .= $output 
     363                      unless ( 
     364                        $text =~ s/(?<!location\=\")($location)/$output$1/m ); 
    327365                } 
    328             } elsif ( $anchor ) { 
     366                else {    # AFTER 
     367                    $text .= $output 
     368                      unless ( 
     369                        $text =~ s/(?<!location\=\")($location)/$1$output/m ); 
     370 
     371                } 
     372                $text .= "\n" unless $text =~ m/\n$/s; 
     373            } 
     374            elsif ($anchor) { 
     375 
    329376                # position relative to anchor 
    330377                if ( $position eq 'BEFORE' ) { 
    331                     $text =~ s/^($anchor\s)/$output$1/m; 
    332                 } else { # AFTER 
    333                     $text =~ s/^($anchor\s)/$1$output/m; 
     378                    $text .= $output 
     379                      unless ( $text =~ s/^($anchor\s)/$output$1/m ); 
    334380                } 
    335             } else { 
     381                else {    # AFTER 
     382                    $text .= $output 
     383                      unless ( $text =~ s/^($anchor\s)/$1$output/m ); 
     384                } 
     385                $text .= "\n" unless $text =~ m/\n$/s; 
     386            } 
     387            else { 
     388 
    336389                # Position relative to index'th comment 
    337390                my $idx = 0; 
    338                 unless( $text =~ s((%COMMENT({.*?})?%.*\n)) 
    339                           (&_nth($1,\$idx,$position,$index,$output))eg ) { 
     391                unless ( 
     392                    $text =~ s((%COMMENT({.*?})?%.*\n)) 
     393                          (&_nth($1,\$idx,$position,$index,$output))eg 
     394                  ) 
     395                { 
     396 
    340397                    # If there was a problem adding relative to the comment, 
    341398                    # add to the end of the topic 
    342399                    $text .= $output; 
    343                 }; 
    344             } 
    345         } 
    346     } 
    347  
    348     if (defined $remove) { 
     400                } 
     401                $text .= "\n" unless $text =~ m/\n$/s; 
     402            } 
     403        } 
     404    } 
     405 
     406    if ( defined $remove ) { 
     407 
    349408        # remove the index'th comment box 
    350409        my $idx = 0; 
     
    359418    my ( $tag, $pidx, $position, $index, $output ) = @_; 
    360419 
    361     if ( $$pidx == $index) { 
     420    if ( $$pidx == $index ) { 
    362421        if ( $position eq 'BEFORE' ) { 
    363             $tag = $output.$tag; 
    364         } else { # AFTER 
     422            $tag = $output . $tag; 
     423        } 
     424        else {    # AFTER 
    365425            $tag .= $output; 
    366426        } 
     
    372432# PRIVATE remove the nth comment box 
    373433sub _remove_nth { 
    374     my( $tag, $pidx, $index ) = @_; 
    375     $tag = '' if( $$pidx == $index); 
     434    my ( $tag, $pidx, $index ) = @_; 
     435    $tag = '' if ( $$pidx == $index ); 
    376436    $$pidx++; 
    377437    return $tag; 
Note: See TracChangeset for help on using the changeset viewer.