]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
Make noopener the default for urlize
authorArmin Ronacher <armin.ronacher@active-4.com>
Fri, 30 Dec 2016 23:40:38 +0000 (00:40 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Fri, 30 Dec 2016 23:40:38 +0000 (00:40 +0100)
CHANGES
jinja2/defaults.py
jinja2/environment.py
jinja2/filters.py
jinja2/utils.py
tests/test_filters.py
tests/test_regression.py

diff --git a/CHANGES b/CHANGES
index 36b0817b549f44fa245f06fcf96f77abd7c23416..aac02effbe309f36f4a100ba3813fd206c7fcd5f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -11,6 +11,8 @@ Version 2.9
 - Corrected a long standing issue with operator precedence of math operations
   not being what was expected.
 - Added support for Python 3.6 async iterators through a new async mode.
+- Added policies for filter defaults and similar things.
+- urlize now sets "rel noopener" by default.
 
 Version 2.8.2
 -------------
index 3717a7223f6882de8393a63e7fdec449c85ee89b..bdb538d59d136ed4319e6504c654d9761369817d 100644 (file)
@@ -39,5 +39,12 @@ DEFAULT_NAMESPACE = {
 }
 
 
+# default policies
+DEFAULT_POLICIES = {
+    'urlize.rel':       'noopener',
+    'urlize.target':    None,
+}
+
+
 # export all constants
 __all__ = tuple(x for x in locals().keys() if x.isupper())
index 7aa9cef88385c82e7149c67d868d45fc0ae98d7b..1aff8f54d726f136d10d22f1e556654083317855 100644 (file)
@@ -18,7 +18,7 @@ from jinja2.defaults import BLOCK_START_STRING, \
      COMMENT_START_STRING, COMMENT_END_STRING, LINE_STATEMENT_PREFIX, \
      LINE_COMMENT_PREFIX, TRIM_BLOCKS, NEWLINE_SEQUENCE, \
      DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE, \
-     KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS
+     DEFAULT_POLICIES, KEEP_TRAILING_NEWLINE, LSTRIP_BLOCKS
 from jinja2.lexer import get_lexer, TokenStream
 from jinja2.parser import Parser
 from jinja2.nodes import EvalContext
@@ -317,6 +317,9 @@ class Environment(object):
         self.bytecode_cache = bytecode_cache
         self.auto_reload = auto_reload
 
+        # configurable policies
+        self.policies = DEFAULT_POLICIES.copy()
+
         # load extensions
         self.extensions = load_extensions(self, extensions)
 
index 2d4597321bd5ba6e025a5ce7c1375cb6379f7320..c93c8ff0b2f32baeaadc0b0735ccdf9061e9f9f6 100644 (file)
@@ -409,7 +409,7 @@ def do_pprint(value, verbose=False):
 
 @evalcontextfilter
 def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False,
-              target=None):
+              target=None, rel=None):
     """Converts URLs in plain text into clickable links.
 
     If you pass the filter an additional integer it will shorten the urls
@@ -431,7 +431,15 @@ def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False,
     .. versionchanged:: 2.8+
        The *target* parameter was added.
     """
-    rv = urlize(value, trim_url_limit, nofollow, target)
+    policies = eval_ctx.environment.policies
+    rel = set((rel or '').split() or [])
+    if nofollow:
+        rel.add('nofollow')
+    rel.update((policies['urlize.rel'] or '').split())
+    if target is None:
+        target = policies['urlize.target']
+    rel = ' '.join(sorted(rel)) or None
+    rv = urlize(value, trim_url_limit, rel=rel, target=target)
     if eval_ctx.autoescape:
         rv = Markup(rv)
     return rv
index 12ae68c7fe752c63b03b8c273356f5808240876a..292dcf06d03876bc4d607129597e313b83a30d24 100644 (file)
@@ -183,7 +183,7 @@ def pformat(obj, verbose=False):
         return pformat(obj)
 
 
-def urlize(text, trim_url_limit=None, nofollow=False, target=None):
+def urlize(text, trim_url_limit=None, rel=None, target=None):
     """Converts any URLs in text into clickable links. Works on http://,
     https:// and www. links. Links can have trailing punctuation (periods,
     commas, close-parens) and leading punctuation (opening parens) and
@@ -201,11 +201,9 @@ def urlize(text, trim_url_limit=None, nofollow=False, target=None):
                          and (x[:limit] + (len(x) >=limit and '...'
                          or '')) or x
     words = _word_split_re.split(text_type(escape(text)))
-    nofollow_attr = nofollow and ' rel="nofollow"' or ''
-    if target is not None and isinstance(target, string_types):
-        target_attr = ' target="%s"' % escape(target)
-    else:
-        target_attr = ''
+    rel_attr = rel and ' rel="%s"' % text_type(escape(rel)) or ''
+    target_attr = target and ' target="%s"' % escape(target) or ''
+
     for i, word in enumerate(words):
         match = _punctuation_re.match(word)
         if match:
@@ -221,11 +219,11 @@ def urlize(text, trim_url_limit=None, nofollow=False, target=None):
                     middle.endswith('.com')
                 )):
                 middle = '<a href="http://%s"%s%s>%s</a>' % (middle,
-                    nofollow_attr, target_attr, trim_url(middle))
+                    rel_attr, target_attr, trim_url(middle))
             if middle.startswith('http://') or \
                middle.startswith('https://'):
                 middle = '<a href="%s"%s%s>%s</a>' % (middle,
-                    nofollow_attr, target_attr, trim_url(middle))
+                    rel_attr, target_attr, trim_url(middle))
             if '@' in middle and not middle.startswith('www.') and \
                not ':' in middle and _simple_email_re.match(middle):
                 middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
index 59999ee4c665afdc1988fbf20bc8ae3ea5e32beb..f8a2f196167fea7147bb450c43d4080ad97cd4c2 100644 (file)
@@ -261,21 +261,28 @@ class TestFilter(object):
     def test_urlize(self, env):
         tmpl = env.from_string(
             '{{ "foo http://www.example.com/ bar"|urlize }}')
-        assert tmpl.render() == 'foo <a href="http://www.example.com/">'\
-                                'http://www.example.com/</a> bar'
+        assert tmpl.render() == (
+            'foo <a href="http://www.example.com/" rel="noopener">'
+            'http://www.example.com/</a> bar'
+        )
+
+    def test_urlize_rel_policy(self):
+        env = Environment()
+        env.policies['urlize.rel'] = None
+        tmpl = env.from_string(
+            '{{ "foo http://www.example.com/ bar"|urlize }}')
+        assert tmpl.render() == (
+            'foo <a href="http://www.example.com/">'
+            'http://www.example.com/</a> bar'
+        )
 
     def test_urlize_target_parameter(self, env):
         tmpl = env.from_string(
             '{{ "foo http://www.example.com/ bar"|urlize(target="_blank") }}'
         )
         assert tmpl.render() \
-            == 'foo <a href="http://www.example.com/" target="_blank">'\
+            == 'foo <a href="http://www.example.com/" rel="noopener" target="_blank">'\
             'http://www.example.com/</a> bar'
-        tmpl = env.from_string(
-            '{{ "foo http://www.example.com/ bar"|urlize(target=42) }}'
-        )
-        assert tmpl.render() == 'foo <a href="http://www.example.com/">'\
-                                'http://www.example.com/</a> bar'
 
     def test_wordcount(self, env):
         tmpl = env.from_string('{{ "foo bar baz"|wordcount }}')
index a4aa157110395fcd24997bc04a7022f706d00560..7b18ef42c3745adfea294a9844560e602b8b150a 100644 (file)
@@ -100,7 +100,7 @@ class TestBug():
 
     def test_urlize_filter_escaping(self, env):
         tmpl = env.from_string('{{ "http://www.example.org/<foo"|urlize }}')
-        assert tmpl.render() == '<a href="http://www.example.org/&lt;foo">'\
+        assert tmpl.render() == '<a href="http://www.example.org/&lt;foo" rel="noopener">'\
             'http://www.example.org/&lt;foo</a>'
 
     def test_loop_call_loop(self, env):