Repository: mozilla/bleach

Issue:
Children of <pre> tags should not be linkified when skip_pre=True (patch attached)
The children of `pre` tags should not be linkified when `skip_pre` is on 

```
diff --git a/bleach/__init__.py b/bleach/__init__.py
index 48b6512..4c2dd1b 100644
--- a/bleach/__init__.py
+++ b/bleach/__init__.py
@@ -300,7 +300,7 @@ def linkify(text, callbacks=DEFAULT_CALLBACKS, skip_pre=False,
                 if node.tag == ETREE_TAG('pre') and skip_pre:
                     linkify_nodes(node, False)
                 elif not (node in _seen):
-                    linkify_nodes(node, True)
+                    linkify_nodes(node, parse_text)
 
             current_child += 1
 
diff --git a/bleach/tests/test_links.py b/bleach/tests/test_links.py
index 62da8d1..ae0fba7 100644
--- a/bleach/tests/test_links.py
+++ b/bleach/tests/test_links.py
@@ -314,6 +314,13 @@ def test_skip_pre():
     eq_(nofollowed, linkify(already_linked))
     eq_(nofollowed, linkify(already_linked, skip_pre=True))
 
+def test_skip_pre_child():
+    # Don't linkify the children of pre tags.
+    intext = '<pre><code>http://foo.com</code></pre>http://bar.com'
+    expect = '<pre><code>http://foo.com</code></pre><a href="http://bar.com" rel="nofollow">http://bar.com</a>'
+    output = linkify(intext, skip_pre=True)
+    eq_(expect, output)
+
 
 def test_libgl():
     """libgl.so.1 should not be linkified."""
```

Comments:

Patch:
diff --git a/bleach/__init__.py b/bleach/__init__.py
index 3092cb7..bf67bf3 100644
--- a/bleach/__init__.py
+++ b/bleach/__init__.py
@@ -2,6 +2,12 @@
 
 from __future__ import unicode_literals
 import logging
+try:  # Python 2.7+
+    from logging import NullHandler
+except ImportError:
+    class NullHandler(logging.Handler):
+        def emit(self, record):
+            pass
 import re
 
 import html5lib
@@ -15,7 +21,8 @@ from .version import __version__, VERSION # flake8: noqa
 
 __all__ = ['clean', 'linkify']
 
-log = logging.getLogger('bleach')
+log = logging.getLogger(__name__)
+log.addHandler(NullHandler())
 
 ALLOWED_TAGS = [
     'a',
@@ -315,7 +322,7 @@ def linkify(text, callbacks=DEFAULT_CALLBACKS, skip_pre=False,
                 if node.tag == ETREE_TAG('pre') and skip_pre:
                     linkify_nodes(node, False)
                 elif not (node in _seen):
-                    linkify_nodes(node, True)
+                    linkify_nodes(node, parse_text)
 
             current_child += 1
 
@@ -344,6 +351,14 @@ def linkify(text, callbacks=DEFAULT_CALLBACKS, skip_pre=False,
         if url.startswith('('):
             _wrapping = strip_wrapping_parentheses(url)
             url, open_brackets, close_brackets = _wrapping
+        if url.endswith(')') and '(' not in url:
+            # This is a clumsy handling for the case where we have something
+            # like (foo http://example.com) and the ) gets picked up by the
+            # url_re but we don't want it part of the link.
+            new_url = url.rstrip(')')
+            close_brackets += len(url) - len(new_url)
+            url = new_url
+
         end = ''
         m = re.search(punct_re, url)
         if m:


Test patch:
diff --git a/bleach/tests/test_links.py b/bleach/tests/test_links.py
index 62da8d1..20d50ac 100644
--- a/bleach/tests/test_links.py
+++ b/bleach/tests/test_links.py
@@ -314,6 +314,13 @@ def test_skip_pre():
     eq_(nofollowed, linkify(already_linked))
     eq_(nofollowed, linkify(already_linked, skip_pre=True))
 
+    eq_(
+        linkify('<pre><code>http://example.com</code></pre>http://example.com',
+                skip_pre=True),
+        ('<pre><code>http://example.com</code></pre>'
+         '<a href="http://example.com" rel="nofollow">http://example.com</a>')
+    )
+
 
 def test_libgl():
     """libgl.so.1 should not be linkified."""
@@ -360,11 +367,16 @@ def test_wrapping_parentheses():
     tests = (
         ('(example.com)', ('(', 'example.com', 'example.com', ')')),
         ('(example.com/)', ('(', 'example.com/', 'example.com/', ')')),
-        ('(example.com/foo)', ('(', 'example.com/foo',
-         'example.com/foo', ')')),
-        ('(((example.com/))))', ('(((', 'example.com/)',
-         'example.com/)', ')))')),
-        ('example.com/))', ('', 'example.com/))', 'example.com/))', '')),
+        ('(example.com/foo)',
+         ('(', 'example.com/foo', 'example.com/foo', ')')),
+        ('(((example.com/))))',
+         ('(((', 'example.com/', 'example.com/', '))))')),
+        ('example.com/))',
+         ('', 'example.com/', 'example.com/', '))')),
+        ('(foo http://example.com/)',
+         ('(foo ', 'example.com/', 'http://example.com/', ')')),
+        ('(foo http://example.com)',
+         ('(foo ', 'example.com', 'http://example.com', ')')),
         ('http://en.wikipedia.org/wiki/Test_(assessment)',
          ('', 'en.wikipedia.org/wiki/Test_(assessment)',
           'http://en.wikipedia.org/wiki/Test_(assessment)', '')),