Kaydet (Commit) 04ee4059 authored tarafından Tim Graham's avatar Tim Graham

Refs #24022 -- Removed the ssi tag per deprecation timeline.

üst 3af9b700
......@@ -99,7 +99,6 @@ class Settings(BaseSettings):
mod = importlib.import_module(self.SETTINGS_MODULE)
tuple_settings = (
"ALLOWED_INCLUDE_ROOTS",
"INSTALLED_APPS",
"TEMPLATE_DIRS",
"LOCALE_PATHS",
......
......@@ -260,10 +260,6 @@ DISALLOWED_USER_AGENTS = []
ABSOLUTE_URL_OVERRIDES = {}
# List of strings representing allowed prefixes for the {% ssi %} tag.
# Example: ['/home/html', '/var/www']
ALLOWED_INCLUDE_ROOTS = []
# List of compiled regular expression objects representing URLs that need not
# be reported by BrokenLinkEmailsMiddleware. Here are a few examples:
# import re
......
......@@ -10,7 +10,6 @@ def check_duplicate_template_settings(app_configs, **kwargs):
if settings.TEMPLATES:
values = [
'TEMPLATE_DIRS',
'ALLOWED_INCLUDE_ROOTS',
'TEMPLATE_CONTEXT_PROCESSORS',
'TEMPLATE_DEBUG',
'TEMPLATE_LOADERS',
......
"""Default tags used by the template system, available to all templates."""
from __future__ import unicode_literals
import os
import re
import sys
import warnings
......@@ -19,7 +18,7 @@ from django.utils.safestring import mark_safe
from .base import (
BLOCK_TAG_END, BLOCK_TAG_START, COMMENT_TAG_END, COMMENT_TAG_START,
SINGLE_BRACE_END, SINGLE_BRACE_START, VARIABLE_ATTRIBUTE_SEPARATOR,
VARIABLE_TAG_END, VARIABLE_TAG_START, Context, Node, NodeList, Template,
VARIABLE_TAG_END, VARIABLE_TAG_START, Context, Node, NodeList,
TemplateSyntaxError, VariableDoesNotExist, kwarg_re,
render_value_in_context, token_kwargs,
)
......@@ -373,44 +372,6 @@ class RegroupNode(Node):
return ''
def include_is_allowed(filepath, allowed_include_roots):
filepath = os.path.abspath(filepath)
for root in allowed_include_roots:
if filepath.startswith(root):
return True
return False
class SsiNode(Node):
def __init__(self, filepath, parsed):
self.filepath = filepath
self.parsed = parsed
def render(self, context):
filepath = self.filepath.resolve(context)
if not include_is_allowed(filepath, context.template.engine.allowed_include_roots):
if settings.DEBUG:
return "[Didn't have permission to include file]"
else:
return '' # Fail silently for invalid includes.
try:
with open(filepath, 'r') as fp:
output = fp.read()
except IOError:
output = ''
if self.parsed:
try:
t = Template(output, name=filepath, engine=context.template.engine)
return t.render(context)
except TemplateSyntaxError as e:
if settings.DEBUG:
return "[Included template had syntax error: %s]" % e
else:
return '' # Fail silently for invalid included templates.
return output
class LoadNode(Node):
def render(self, context):
return ''
......@@ -1091,42 +1052,6 @@ def ifchanged(parser, token):
return IfChangedNode(nodelist_true, nodelist_false, *values)
@register.tag
def ssi(parser, token):
"""
Outputs the contents of a given file into the page.
Like a simple "include" tag, the ``ssi`` tag includes the contents
of another file -- which must be specified using an absolute path --
in the current page::
{% ssi "/home/html/ljworld.com/includes/right_generic.html" %}
If the optional "parsed" parameter is given, the contents of the included
file are evaluated as template code, with the current context::
{% ssi "/home/html/ljworld.com/includes/right_generic.html" parsed %}
"""
warnings.warn(
"The {% ssi %} tag is deprecated. Use the {% include %} tag instead.",
RemovedInDjango110Warning,
)
bits = token.split_contents()
parsed = False
if len(bits) not in (2, 3):
raise TemplateSyntaxError("'ssi' tag takes one argument: the path to"
" the file to be included")
if len(bits) == 3:
if bits[2] == 'parsed':
parsed = True
else:
raise TemplateSyntaxError("Second (optional) argument to %s tag"
" must be 'parsed'" % bits[0])
filepath = parser.compile_filter(bits[1])
return SsiNode(filepath, parsed)
def find_library(parser, name):
try:
return parser.libraries[name]
......
......@@ -23,14 +23,11 @@ class Engine(object):
'django.template.loader_tags',
]
def __init__(self, dirs=None, app_dirs=False,
allowed_include_roots=None, context_processors=None,
def __init__(self, dirs=None, app_dirs=False, context_processors=None,
debug=False, loaders=None, string_if_invalid='',
file_charset='utf-8', libraries=None, builtins=None):
if dirs is None:
dirs = []
if allowed_include_roots is None:
allowed_include_roots = []
if context_processors is None:
context_processors = []
if loaders is None:
......@@ -46,13 +43,8 @@ class Engine(object):
if builtins is None:
builtins = []
if isinstance(allowed_include_roots, six.string_types):
raise ImproperlyConfigured(
"allowed_include_roots must be a tuple, not a string.")
self.dirs = dirs
self.app_dirs = app_dirs
self.allowed_include_roots = allowed_include_roots
self.context_processors = context_processors
self.debug = debug
self.loaders = loaders
......
......@@ -40,7 +40,6 @@ class EngineHandler(object):
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': settings.TEMPLATE_DIRS,
'OPTIONS': {
'allowed_include_roots': settings.ALLOWED_INCLUDE_ROOTS,
'context_processors': settings.TEMPLATE_CONTEXT_PROCESSORS,
'debug': settings.TEMPLATE_DEBUG,
'loaders': settings.TEMPLATE_LOADERS,
......
......@@ -85,7 +85,6 @@ def reset_template_engines(**kwargs):
if kwargs['setting'] in {
'TEMPLATES',
'TEMPLATE_DIRS',
'ALLOWED_INCLUDE_ROOTS',
'TEMPLATE_CONTEXT_PROCESSORS',
'TEMPLATE_DEBUG',
'TEMPLATE_LOADERS',
......
......@@ -691,10 +691,10 @@ details on these changes.
handling. (This is faster than the usual deprecation path; see the
:doc:`Django 1.4 release notes</releases/1.4>`.)
* The :ttag:`url` and :ttag:`ssi` template tags will be
modified so that the first argument to each tag is a template variable, not
an implied string. In 1.4, this behavior is provided by a version of the tag
in the ``future`` template tag library.
* The ``url`` and ``ssi`` template tags will be modified so that the first
argument to each tag is a template variable, not an implied string. In 1.4,
this behavior is provided by a version of the tag in the ``future`` template
tag library.
* The ``reset`` and ``sqlreset`` management commands will be removed.
......
......@@ -252,9 +252,9 @@ that might occur as a result of a version upgrade.
* **1_8.W001**: The standalone ``TEMPLATE_*`` settings were deprecated in
Django 1.8 and the :setting:`TEMPLATES` dictionary takes precedence. You must
put the values of the following settings into your defaults ``TEMPLATES``
dict: :setting:`TEMPLATE_DIRS`, :setting:`ALLOWED_INCLUDE_ROOTS`,
:setting:`TEMPLATE_CONTEXT_PROCESSORS`, :setting:`TEMPLATE_DEBUG`,
:setting:`TEMPLATE_LOADERS`, :setting:`TEMPLATE_STRING_IF_INVALID`.
dict: :setting:`TEMPLATE_DIRS`, :setting:`TEMPLATE_CONTEXT_PROCESSORS`,
:setting:`TEMPLATE_DEBUG`, :setting:`TEMPLATE_LOADERS`,
:setting:`TEMPLATE_STRING_IF_INVALID`.
Admin
-----
......
......@@ -99,32 +99,6 @@ This validation only applies via :meth:`~django.http.HttpRequest.get_host()`;
if your code accesses the ``Host`` header directly from ``request.META`` you
are bypassing this security protection.
.. setting:: ALLOWED_INCLUDE_ROOTS
ALLOWED_INCLUDE_ROOTS
---------------------
Default: ``[]`` (Empty list)
.. deprecated:: 1.8
This setting, along with the :ttag:`ssi` template tag, is deprecated and
will be removed in Django 1.10.
.. versionchanged:: 1.8
You can also set the ``'allowed_include_roots'`` option in the
:setting:`OPTIONS <TEMPLATES-OPTIONS>` of a ``DjangoTemplates`` backend
instead.
A list of strings representing allowed prefixes for the ``{% ssi %}`` template
tag. This is a security measure, so that template authors can't access files
that they shouldn't be accessing.
For example, if :setting:`ALLOWED_INCLUDE_ROOTS` is ``['/home/html', '/var/www']``,
then ``{% ssi /home/html/foo.txt %}`` would work, but ``{% ssi /etc/passwd %}``
wouldn't.
.. setting:: APPEND_SLASH
APPEND_SLASH
......@@ -3433,7 +3407,6 @@ Serialization
Templates
---------
* :setting:`ALLOWED_INCLUDE_ROOTS`
* :setting:`TEMPLATES`
* :setting:`TEMPLATE_CONTEXT_PROCESSORS`
* :setting:`TEMPLATE_DEBUG`
......
......@@ -48,7 +48,7 @@ probably isn't the documentation you're looking for. An instance of the
of that backend and any attribute defaults mentioned below are overridden by
what's passed by :class:`~django.template.backends.django.DjangoTemplates`.
.. class:: Engine(dirs=None, app_dirs=False, allowed_include_roots=None, context_processors=None, debug=False, loaders=None, string_if_invalid='', file_charset='utf-8', libraries=None, builtins=None)
.. class:: Engine(dirs=None, app_dirs=False, context_processors=None, debug=False, loaders=None, string_if_invalid='', file_charset='utf-8', libraries=None, builtins=None)
.. versionadded:: 1.8
......@@ -65,21 +65,6 @@ what's passed by :class:`~django.template.backends.django.DjangoTemplates`.
It defaults to ``False``.
* ``allowed_include_roots`` is a list of strings representing allowed
prefixes for the ``{% ssi %}`` template tag. This is a security measure,
so that template authors can't access files that they shouldn't be
accessing.
For example, if ``'allowed_include_roots'`` is ``['/home/html',
'/var/www']``, then ``{% ssi /home/html/foo.txt %}`` would work, but ``{%
ssi /etc/passwd %}`` wouldn't.
It defaults to an empty list.
.. deprecated:: 1.8
``allowed_include_roots`` is deprecated.
* ``context_processors`` is a list of dotted Python paths to callables
that are used to populate the context when a template is rendered with a
request. These callables take a request object as their argument and
......
......@@ -958,45 +958,6 @@ this example, the space around ``Hello`` won't be stripped::
</strong>
{% endspaceless %}
.. templatetag:: ssi
ssi
^^^
.. deprecated:: 1.8
This tag has been deprecated and will be removed in Django 1.10. Use the
:ttag:`include` tag instead.
Outputs the contents of a given file into the page.
Like a simple :ttag:`include` tag, ``{% ssi %}`` includes the contents of
another file -- which must be specified using an absolute path -- in the
current page::
{% ssi '/home/html/ljworld.com/includes/right_generic.html' %}
The first parameter of ``ssi`` can be a quoted literal or any other context
variable.
If the optional ``parsed`` parameter is given, the contents of the included
file are evaluated as template code, within the current context::
{% ssi '/home/html/ljworld.com/includes/right_generic.html' parsed %}
Note that if you use ``{% ssi %}``, you'll need to define
``'allowed_include_roots'`` in the :setting:`OPTIONS <TEMPLATES-OPTIONS>` of
your template engine, as a security measure.
.. note::
With the :ttag:`ssi` tag and the ``parsed`` parameter
there is no shared state between files -- each include is a completely
independent rendering process. This means it's not possible for example to
define blocks or alter the context in the current page using the included
file.
See also: :ttag:`{% include %}<include>`.
.. templatetag:: templatetag
templatetag
......
......@@ -730,8 +730,8 @@ and Ctrl-C test termination) have been made redundant. In view of this
redundancy, ``DjangoTestRunner`` has been turned into an empty placeholder
class, and will be removed entirely in Django 1.5.
Changes to :ttag:`url` and :ttag:`ssi`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Changes to ``url`` and ``ssi``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Most template tags will allow you to pass in either constants or
variables as arguments -- for example::
......@@ -745,15 +745,14 @@ context variable ``templ`` that contains the value ``base.html``::
is also legal.
However, due to an accident of history, the :ttag:`url` and
:ttag:`ssi` are different. These tags use the second, quoteless
syntax, but interpret the argument as a constant. This means it isn't
possible to use a context variable as the target of a :ttag:`url` and
:ttag:`ssi` tag.
However, due to an accident of history, the ``url`` and ``ssi`` are different.
These tags use the second, quoteless syntax, but interpret the argument as a
constant. This means it isn't possible to use a context variable as the target
of a ``url`` and ``ssi`` tag.
Django 1.3 marks the start of the process to correct this historical
accident. Django 1.3 adds a new template library -- ``future`` -- that
provides alternate implementations of the :ttag:`url` and :ttag:`ssi`
provides alternate implementations of the ``url`` and ``ssi``
template tags. This ``future`` library implement behavior that makes
the handling of the first argument consistent with the handling of all
other variables. So, an existing template that contains::
......
......@@ -7,11 +7,11 @@ Django 1.4.7 release notes
Django 1.4.7 fixes one security issue present in previous Django releases in
the 1.4 series.
Directory traversal vulnerability in :ttag:`ssi` template tag
-------------------------------------------------------------
Directory traversal vulnerability in ``ssi`` template tag
---------------------------------------------------------
In previous versions of Django it was possible to bypass the
:setting:`ALLOWED_INCLUDE_ROOTS` setting used for security with the :ttag:`ssi`
``ALLOWED_INCLUDE_ROOTS`` setting used for security with the ``ssi``
template tag by specifying a relative path that starts with one of the allowed
roots. For example, if ``ALLOWED_INCLUDE_ROOTS = ("/var/www",)`` the following
would be possible:
......@@ -21,5 +21,5 @@ would be possible:
{% ssi "/var/www/../../etc/passwd" %}
In practice this is not a very common problem, as it would require the template
author to put the :ttag:`ssi` file in a user-controlled variable, but it's
author to put the ``ssi`` file in a user-controlled variable, but it's
possible in principle.
......@@ -8,11 +8,11 @@ This is Django 1.5.3, the third release in the Django 1.5 series. It addresses
one security issue and also contains an opt-in feature to enhance the security
of :mod:`django.contrib.sessions`.
Directory traversal vulnerability in :ttag:`ssi` template tag
-------------------------------------------------------------
Directory traversal vulnerability in ``ssi`` template tag
---------------------------------------------------------
In previous versions of Django it was possible to bypass the
:setting:`ALLOWED_INCLUDE_ROOTS` setting used for security with the :ttag:`ssi`
``ALLOWED_INCLUDE_ROOTS`` setting used for security with the ``ssi``
template tag by specifying a relative path that starts with one of the allowed
roots. For example, if ``ALLOWED_INCLUDE_ROOTS = ("/var/www",)`` the following
would be possible:
......@@ -22,8 +22,8 @@ would be possible:
{% ssi "/var/www/../../etc/passwd" %}
In practice this is not a very common problem, as it would require the template
author to put the :ttag:`ssi` file in a user-controlled variable, but it's
possible in principle.
author to put the ``ssi`` file in a user-controlled variable, but it's possible
in principle.
Mitigating a remote-code execution vulnerability in :mod:`django.contrib.sessions`
----------------------------------------------------------------------------------
......
......@@ -1734,7 +1734,7 @@ Loading ``ssi`` and ``url`` template tags from ``future`` library
Django 1.3 introduced ``{% load ssi from future %}`` and
``{% load url from future %}`` syntax for forward compatibility of the
:ttag:`ssi` and :ttag:`url` template tags. This syntax is now deprecated and
``ssi`` and :ttag:`url` template tags. This syntax is now deprecated and
will be removed in Django 1.9. You can simply remove the
``{% load ... from future %}`` tags.
......
......@@ -1654,7 +1654,7 @@ Django 1.10.
``ssi`` template tag
~~~~~~~~~~~~~~~~~~~~
The :ttag:`ssi` template tag allows files to be included in a template by
The ``ssi`` template tag allows files to be included in a template by
absolute path. This is of limited use in most deployment situations, and
the :ttag:`include` tag often makes more sense. This tag is now deprecated and
will be removed in Django 1.10.
......
......@@ -348,21 +348,6 @@ applications. This generic name was kept for backwards-compatibility.
``DjangoTemplates`` engines accept the following :setting:`OPTIONS
<TEMPLATES-OPTIONS>`:
* ``'allowed_include_roots'``: a list of strings representing allowed prefixes
for the ``{% ssi %}`` template tag. This is a security measure, so that
template authors can't access files that they shouldn't be accessing.
For example, if ``'allowed_include_roots'`` is ``['/home/html',
'/var/www']``, then ``{% ssi /home/html/foo.txt %}`` would work, but ``{%
ssi /etc/passwd %}`` wouldn't.
It defaults to an empty list.
.. deprecated:: 1.8
``allowed_include_roots`` is deprecated because the {% ssi %} tag is
deprecated.
* ``'context_processors'``: a list of dotted Python paths to callables that
are used to populate the context when a template is rendered with a request.
These callables take a request object as their argument and return a
......
......@@ -472,7 +472,6 @@ class TestListSettings(unittest.TestCase):
ImproperlyConfigured if they are set to a string instead of a list or tuple.
"""
list_or_tuple_settings = (
"ALLOWED_INCLUDE_ROOTS",
"INSTALLED_APPS",
"TEMPLATE_DIRS",
"LOCALE_PATHS",
......
from __future__ import unicode_literals
import os
from django.template import Context, Engine
from django.test import SimpleTestCase, ignore_warnings
from django.utils.deprecation import RemovedInDjango110Warning
from ..utils import ROOT, setup
@ignore_warnings(category=RemovedInDjango110Warning)
class SsiTagTests(SimpleTestCase):
# Test normal behavior
@setup({'ssi01': '{%% ssi "%s" %%}' % os.path.join(
ROOT, 'templates', 'ssi_include.html',
)})
def test_ssi01(self):
output = self.engine.render_to_string('ssi01')
self.assertEqual(output, 'This is for testing an ssi include. {{ test }}\n')
@setup({'ssi02': '{%% ssi "%s" %%}' % os.path.join(
ROOT, 'not_here',
)})
def test_ssi02(self):
output = self.engine.render_to_string('ssi02')
self.assertEqual(output, ''),
@setup({'ssi03': "{%% ssi '%s' %%}" % os.path.join(
ROOT, 'not_here',
)})
def test_ssi03(self):
output = self.engine.render_to_string('ssi03')
self.assertEqual(output, ''),
# Test passing as a variable
@setup({'ssi04': '{% ssi ssi_file %}'})
def test_ssi04(self):
output = self.engine.render_to_string('ssi04', {
'ssi_file': os.path.join(ROOT, 'templates', 'ssi_include.html')
})
self.assertEqual(output, 'This is for testing an ssi include. {{ test }}\n')
@setup({'ssi05': '{% ssi ssi_file %}'})
def test_ssi05(self):
output = self.engine.render_to_string('ssi05', {'ssi_file': 'no_file'})
self.assertEqual(output, '')
# Test parsed output
@setup({'ssi06': '{%% ssi "%s" parsed %%}' % os.path.join(
ROOT, 'templates', 'ssi_include.html',
)})
def test_ssi06(self):
output = self.engine.render_to_string('ssi06', {'test': 'Look ma! It parsed!'})
self.assertEqual(output, 'This is for testing an ssi include. '
'Look ma! It parsed!\n')
@setup({'ssi07': '{%% ssi "%s" parsed %%}' % os.path.join(
ROOT, 'not_here',
)})
def test_ssi07(self):
output = self.engine.render_to_string('ssi07', {'test': 'Look ma! It parsed!'})
self.assertEqual(output, '')
# Test space in file name
@setup({'ssi08': '{%% ssi "%s" %%}' % os.path.join(
ROOT, 'templates', 'ssi include with spaces.html',
)})
def test_ssi08(self):
output = self.engine.render_to_string('ssi08')
self.assertEqual(output, 'This is for testing an ssi include '
'with spaces in its name. {{ test }}\n')
@setup({'ssi09': '{%% ssi "%s" parsed %%}' % os.path.join(
ROOT, 'templates', 'ssi include with spaces.html',
)})
def test_ssi09(self):
output = self.engine.render_to_string('ssi09', {'test': 'Look ma! It parsed!'})
self.assertEqual(output, 'This is for testing an ssi include '
'with spaces in its name. Look ma! It parsed!\n')
@ignore_warnings(category=RemovedInDjango110Warning)
class SSISecurityTests(SimpleTestCase):
def setUp(self):
self.ssi_dir = os.path.join(ROOT, "templates", "first")
self.engine = Engine(allowed_include_roots=(self.ssi_dir,))
def render_ssi(self, path):
# the path must exist for the test to be reliable
self.assertTrue(os.path.exists(path))
return self.engine.from_string('{%% ssi "%s" %%}' % path).render(Context({}))
def test_allowed_paths(self):
acceptable_path = os.path.join(self.ssi_dir, "..", "first", "test.html")
self.assertEqual(self.render_ssi(acceptable_path), 'First template\n')
def test_relative_include_exploit(self):
"""
May not bypass allowed_include_roots with relative paths
e.g. if allowed_include_roots = ("/var/www",), it should not be
possible to do {% ssi "/var/www/../../etc/passwd" %}
"""
disallowed_paths = [
os.path.join(self.ssi_dir, "..", "ssi_include.html"),
os.path.join(self.ssi_dir, "..", "second", "test.html"),
]
for disallowed_path in disallowed_paths:
self.assertEqual(self.render_ssi(disallowed_path), '')
......@@ -55,7 +55,6 @@ def setup(templates, *args, **kwargs):
libraries = getattr(self, 'libraries', {})
self.engine = Engine(
allowed_include_roots=[ROOT],
libraries=libraries,
loaders=loaders,
)
......@@ -65,7 +64,6 @@ def setup(templates, *args, **kwargs):
func(self)
self.engine = Engine(
allowed_include_roots=[ROOT],
libraries=libraries,
loaders=loaders,
string_if_invalid='INVALID',
......@@ -74,7 +72,6 @@ def setup(templates, *args, **kwargs):
func(self)
self.engine = Engine(
allowed_include_roots=[ROOT],
debug=True,
libraries=libraries,
loaders=loaders,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment