Handle cases where closing tags are out of order, causing extra closing 
tags that are added to break the page.

        Patch by fu. 

diffs (122 lines):

diff -r 1ef62983473f -r 943d1d3d0bc4 cgi-bin/LJ/CleanHTML.pm
--- a/cgi-bin/LJ/CleanHTML.pm   Fri Dec 17 15:06:28 2010 +0800
+++ b/cgi-bin/LJ/CleanHTML.pm   Fri Dec 17 15:27:29 2010 +0800
@@ -237,6 +237,8 @@ sub clean
 
     my $capturing_during_eat;  # if we save all tokens that happen inside the 
eating.
     my @capture = ();  # if so, they go here
+
+    my @tagstack = (); # so we can make sure that tags are closed properly/in 
order
 
     my $form_tag = {
         input => 1,
@@ -886,6 +888,12 @@ sub clean
                                 }
                             }
 
+                            # we have all this previous logic which makes us
+                            # not automatically close tags inside tables
+                            # so rather than mess with it, let's just ignore 
those
+                            # and only deal with non-self-closing tags
+                            # which are not in a table
+                            push @tagstack, $tag unless $slashclose || 
@tablescope;
                         }
                         else { $newdata .= ">"; }
                     }
@@ -970,7 +978,6 @@ sub clean
 
                 if ($allow && ! $remove{$tag})
                 {
-
                     if ($opts->{'tablecheck'}) {
 
                         $allow = 0 if
@@ -983,6 +990,14 @@ sub clean
                     }
 
                     if ($allow && ! ($opts->{'noearlyclose'} && ! 
$opencount{$tag})) {
+
+                        unless ( @tablescope ) {
+                            my $close;
+                            while ( ( $close = pop @tagstack ) && $close ne 
$tag ) {
+                                $opencount{$close}--;
+                                $newdata .= "</$close>";
+                            }
+                        }
 
                         # maintain current table scope
                         if ($opts->{'tablecheck'}) {
@@ -997,8 +1012,10 @@ sub clean
                             }
                         }
 
-                        $newdata .= "</$tag>";
-                        $opencount{$tag}--;
+                        if ( $opencount{$tag} ) {
+                            $newdata .= "</$tag>";
+                            $opencount{$tag}--;
+                        }
                     } else {
                         $newdata .= "&lt;/$tag&gt;";
                     }
diff -r 1ef62983473f -r 943d1d3d0bc4 t/clean-event.t
--- a/t/clean-event.t   Fri Dec 17 15:06:28 2010 +0800
+++ b/t/clean-event.t   Fri Dec 17 15:27:29 2010 +0800
@@ -16,6 +16,49 @@ my $clean = sub {
     LJ::CleanHTML::clean_event(\$orig_post, $opts);
 };
 
+note("malformed html");
+$orig_post  = qq{<div><span>abc</div>};
+$clean_post = qq{<div><span>abc</span></div>};
+$clean->();
+is( $orig_post, $clean_post, "Inner tag isn't closed" );
+
+$orig_post  = qq{<div><table><tr><td></td></tr></table><span></div>};
+$clean_post = qq{<div><table><tr><td></td></tr></table><span></span></div>};
+$clean->();
+is( $orig_post, $clean_post, "Tag outside a table isn't closed" );
+
+# this is a bit weird; we don't want to mess with tags in tables
+# so we just close it after, leading to this HTML
+$orig_post  = qq{<div><table><tr><td><span></td></tr></table></div>};
+$clean_post = qq{<div><table><tr><td><span></td></tr></table></div></span>};
+$clean->();
+is( $orig_post, $clean_post, "Non-table-related tag inside a table is open" );
+
+$orig_post  = qq{<div><table><tr><td></tr></table></div>};
+$clean_post = qq{<div><table><tr><td></tr></table></div>};
+$clean->();
+is( $orig_post, $clean_post, "Table-related-tag inside a table is open" );
+
+$orig_post  = qq{<div><img /></div>};
+$clean_post = qq{<div><img /></div>};
+$clean->();
+is( $orig_post, $clean_post, "Slash-closed tag" );
+
+$orig_post  = qq{<div><span>};
+$clean_post = qq{<div><span></span></div>};
+$clean->();
+is( $orig_post, $clean_post, "No closing tags" );
+
+# in this case, we consider the <span> within the div as unclosed
+# and the closing </span> as extra/unrelated.
+# Therefore, we close the opening tag (which needs to be closed)
+# and escape the closing tag (which has no opening tag)
+$orig_post  = qq{<div><span></div></span>};
+$clean_post = qq{<div><span></span></div>&lt;/span&gt;};
+$clean->();
+is( $orig_post, $clean_post, "Wrong closing tag order" );
+
+note("unwanted tags and attributes");
 # remove header tags
 $orig_post = qq{<h1>test</h1>testing this<h2>testing again</h2>};
 $clean_post = qq{testtesting thistesting again};
@@ -64,6 +107,7 @@ ok($orig_post =~ /^$clean_post$/, "CSS c
 $clean->({ remove_fonts => 1, remove_sizes => 1 });
 ok($orig_post =~ /^$clean_post$/, "CSS fonts and sizes removed");
 
+note("cut tags");
 # get cut text
 my $cut_text;
 my $entry_text = qq{<cut text="first">111</cut><cut text="second">2222</cut>};
_______________________________________________
dw-changelog mailing list
[email protected]
http://lists.dwscoalition.org/mailman/listinfo/dw-changelog

Reply via email to