Refs #24022 -- Removed the ssi tag per deprecation timeline.
This commit is contained in:
parent
3af9b70028
commit
04ee4059d7
|
@ -99,7 +99,6 @@ class Settings(BaseSettings):
|
||||||
mod = importlib.import_module(self.SETTINGS_MODULE)
|
mod = importlib.import_module(self.SETTINGS_MODULE)
|
||||||
|
|
||||||
tuple_settings = (
|
tuple_settings = (
|
||||||
"ALLOWED_INCLUDE_ROOTS",
|
|
||||||
"INSTALLED_APPS",
|
"INSTALLED_APPS",
|
||||||
"TEMPLATE_DIRS",
|
"TEMPLATE_DIRS",
|
||||||
"LOCALE_PATHS",
|
"LOCALE_PATHS",
|
||||||
|
|
|
@ -260,10 +260,6 @@ DISALLOWED_USER_AGENTS = []
|
||||||
|
|
||||||
ABSOLUTE_URL_OVERRIDES = {}
|
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
|
# List of compiled regular expression objects representing URLs that need not
|
||||||
# be reported by BrokenLinkEmailsMiddleware. Here are a few examples:
|
# be reported by BrokenLinkEmailsMiddleware. Here are a few examples:
|
||||||
# import re
|
# import re
|
||||||
|
|
|
@ -10,7 +10,6 @@ def check_duplicate_template_settings(app_configs, **kwargs):
|
||||||
if settings.TEMPLATES:
|
if settings.TEMPLATES:
|
||||||
values = [
|
values = [
|
||||||
'TEMPLATE_DIRS',
|
'TEMPLATE_DIRS',
|
||||||
'ALLOWED_INCLUDE_ROOTS',
|
|
||||||
'TEMPLATE_CONTEXT_PROCESSORS',
|
'TEMPLATE_CONTEXT_PROCESSORS',
|
||||||
'TEMPLATE_DEBUG',
|
'TEMPLATE_DEBUG',
|
||||||
'TEMPLATE_LOADERS',
|
'TEMPLATE_LOADERS',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"""Default tags used by the template system, available to all templates."""
|
"""Default tags used by the template system, available to all templates."""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -19,7 +18,7 @@ from django.utils.safestring import mark_safe
|
||||||
from .base import (
|
from .base import (
|
||||||
BLOCK_TAG_END, BLOCK_TAG_START, COMMENT_TAG_END, COMMENT_TAG_START,
|
BLOCK_TAG_END, BLOCK_TAG_START, COMMENT_TAG_END, COMMENT_TAG_START,
|
||||||
SINGLE_BRACE_END, SINGLE_BRACE_START, VARIABLE_ATTRIBUTE_SEPARATOR,
|
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,
|
TemplateSyntaxError, VariableDoesNotExist, kwarg_re,
|
||||||
render_value_in_context, token_kwargs,
|
render_value_in_context, token_kwargs,
|
||||||
)
|
)
|
||||||
|
@ -373,44 +372,6 @@ class RegroupNode(Node):
|
||||||
return ''
|
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):
|
class LoadNode(Node):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
return ''
|
return ''
|
||||||
|
@ -1091,42 +1052,6 @@ def ifchanged(parser, token):
|
||||||
return IfChangedNode(nodelist_true, nodelist_false, *values)
|
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):
|
def find_library(parser, name):
|
||||||
try:
|
try:
|
||||||
return parser.libraries[name]
|
return parser.libraries[name]
|
||||||
|
|
|
@ -23,14 +23,11 @@ class Engine(object):
|
||||||
'django.template.loader_tags',
|
'django.template.loader_tags',
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, dirs=None, app_dirs=False,
|
def __init__(self, dirs=None, app_dirs=False, context_processors=None,
|
||||||
allowed_include_roots=None, context_processors=None,
|
|
||||||
debug=False, loaders=None, string_if_invalid='',
|
debug=False, loaders=None, string_if_invalid='',
|
||||||
file_charset='utf-8', libraries=None, builtins=None):
|
file_charset='utf-8', libraries=None, builtins=None):
|
||||||
if dirs is None:
|
if dirs is None:
|
||||||
dirs = []
|
dirs = []
|
||||||
if allowed_include_roots is None:
|
|
||||||
allowed_include_roots = []
|
|
||||||
if context_processors is None:
|
if context_processors is None:
|
||||||
context_processors = []
|
context_processors = []
|
||||||
if loaders is None:
|
if loaders is None:
|
||||||
|
@ -46,13 +43,8 @@ class Engine(object):
|
||||||
if builtins is None:
|
if builtins is None:
|
||||||
builtins = []
|
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.dirs = dirs
|
||||||
self.app_dirs = app_dirs
|
self.app_dirs = app_dirs
|
||||||
self.allowed_include_roots = allowed_include_roots
|
|
||||||
self.context_processors = context_processors
|
self.context_processors = context_processors
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.loaders = loaders
|
self.loaders = loaders
|
||||||
|
|
|
@ -40,7 +40,6 @@ class EngineHandler(object):
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': settings.TEMPLATE_DIRS,
|
'DIRS': settings.TEMPLATE_DIRS,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'allowed_include_roots': settings.ALLOWED_INCLUDE_ROOTS,
|
|
||||||
'context_processors': settings.TEMPLATE_CONTEXT_PROCESSORS,
|
'context_processors': settings.TEMPLATE_CONTEXT_PROCESSORS,
|
||||||
'debug': settings.TEMPLATE_DEBUG,
|
'debug': settings.TEMPLATE_DEBUG,
|
||||||
'loaders': settings.TEMPLATE_LOADERS,
|
'loaders': settings.TEMPLATE_LOADERS,
|
||||||
|
|
|
@ -85,7 +85,6 @@ def reset_template_engines(**kwargs):
|
||||||
if kwargs['setting'] in {
|
if kwargs['setting'] in {
|
||||||
'TEMPLATES',
|
'TEMPLATES',
|
||||||
'TEMPLATE_DIRS',
|
'TEMPLATE_DIRS',
|
||||||
'ALLOWED_INCLUDE_ROOTS',
|
|
||||||
'TEMPLATE_CONTEXT_PROCESSORS',
|
'TEMPLATE_CONTEXT_PROCESSORS',
|
||||||
'TEMPLATE_DEBUG',
|
'TEMPLATE_DEBUG',
|
||||||
'TEMPLATE_LOADERS',
|
'TEMPLATE_LOADERS',
|
||||||
|
|
|
@ -691,10 +691,10 @@ details on these changes.
|
||||||
handling. (This is faster than the usual deprecation path; see the
|
handling. (This is faster than the usual deprecation path; see the
|
||||||
:doc:`Django 1.4 release notes</releases/1.4>`.)
|
:doc:`Django 1.4 release notes</releases/1.4>`.)
|
||||||
|
|
||||||
* The :ttag:`url` and :ttag:`ssi` template tags will be
|
* The ``url`` and ``ssi`` template tags will be modified so that the first
|
||||||
modified so that the first argument to each tag is a template variable, not
|
argument to each tag is a template variable, not an implied string. In 1.4,
|
||||||
an implied string. In 1.4, this behavior is provided by a version of the tag
|
this behavior is provided by a version of the tag in the ``future`` template
|
||||||
in the ``future`` template tag library.
|
tag library.
|
||||||
|
|
||||||
* The ``reset`` and ``sqlreset`` management commands will be removed.
|
* 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
|
* **1_8.W001**: The standalone ``TEMPLATE_*`` settings were deprecated in
|
||||||
Django 1.8 and the :setting:`TEMPLATES` dictionary takes precedence. You must
|
Django 1.8 and the :setting:`TEMPLATES` dictionary takes precedence. You must
|
||||||
put the values of the following settings into your defaults ``TEMPLATES``
|
put the values of the following settings into your defaults ``TEMPLATES``
|
||||||
dict: :setting:`TEMPLATE_DIRS`, :setting:`ALLOWED_INCLUDE_ROOTS`,
|
dict: :setting:`TEMPLATE_DIRS`, :setting:`TEMPLATE_CONTEXT_PROCESSORS`,
|
||||||
:setting:`TEMPLATE_CONTEXT_PROCESSORS`, :setting:`TEMPLATE_DEBUG`,
|
:setting:`TEMPLATE_DEBUG`, :setting:`TEMPLATE_LOADERS`,
|
||||||
:setting:`TEMPLATE_LOADERS`, :setting:`TEMPLATE_STRING_IF_INVALID`.
|
:setting:`TEMPLATE_STRING_IF_INVALID`.
|
||||||
|
|
||||||
Admin
|
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
|
if your code accesses the ``Host`` header directly from ``request.META`` you
|
||||||
are bypassing this security protection.
|
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
|
.. setting:: APPEND_SLASH
|
||||||
|
|
||||||
APPEND_SLASH
|
APPEND_SLASH
|
||||||
|
@ -3433,7 +3407,6 @@ Serialization
|
||||||
|
|
||||||
Templates
|
Templates
|
||||||
---------
|
---------
|
||||||
* :setting:`ALLOWED_INCLUDE_ROOTS`
|
|
||||||
* :setting:`TEMPLATES`
|
* :setting:`TEMPLATES`
|
||||||
* :setting:`TEMPLATE_CONTEXT_PROCESSORS`
|
* :setting:`TEMPLATE_CONTEXT_PROCESSORS`
|
||||||
* :setting:`TEMPLATE_DEBUG`
|
* :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
|
of that backend and any attribute defaults mentioned below are overridden by
|
||||||
what's passed by :class:`~django.template.backends.django.DjangoTemplates`.
|
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
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
|
@ -65,21 +65,6 @@ what's passed by :class:`~django.template.backends.django.DjangoTemplates`.
|
||||||
|
|
||||||
It defaults to ``False``.
|
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
|
* ``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
|
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
|
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>
|
</strong>
|
||||||
{% endspaceless %}
|
{% 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:: 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
|
redundancy, ``DjangoTestRunner`` has been turned into an empty placeholder
|
||||||
class, and will be removed entirely in Django 1.5.
|
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
|
Most template tags will allow you to pass in either constants or
|
||||||
variables as arguments -- for example::
|
variables as arguments -- for example::
|
||||||
|
@ -745,15 +745,14 @@ context variable ``templ`` that contains the value ``base.html``::
|
||||||
|
|
||||||
is also legal.
|
is also legal.
|
||||||
|
|
||||||
However, due to an accident of history, the :ttag:`url` and
|
However, due to an accident of history, the ``url`` and ``ssi`` are different.
|
||||||
:ttag:`ssi` are different. These tags use the second, quoteless
|
These tags use the second, quoteless syntax, but interpret the argument as a
|
||||||
syntax, but interpret the argument as a constant. This means it isn't
|
constant. This means it isn't possible to use a context variable as the target
|
||||||
possible to use a context variable as the target of a :ttag:`url` and
|
of a ``url`` and ``ssi`` tag.
|
||||||
:ttag:`ssi` tag.
|
|
||||||
|
|
||||||
Django 1.3 marks the start of the process to correct this historical
|
Django 1.3 marks the start of the process to correct this historical
|
||||||
accident. Django 1.3 adds a new template library -- ``future`` -- that
|
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
|
template tags. This ``future`` library implement behavior that makes
|
||||||
the handling of the first argument consistent with the handling of all
|
the handling of the first argument consistent with the handling of all
|
||||||
other variables. So, an existing template that contains::
|
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
|
Django 1.4.7 fixes one security issue present in previous Django releases in
|
||||||
the 1.4 series.
|
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
|
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
|
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
|
roots. For example, if ``ALLOWED_INCLUDE_ROOTS = ("/var/www",)`` the following
|
||||||
would be possible:
|
would be possible:
|
||||||
|
@ -21,5 +21,5 @@ would be possible:
|
||||||
{% ssi "/var/www/../../etc/passwd" %}
|
{% ssi "/var/www/../../etc/passwd" %}
|
||||||
|
|
||||||
In practice this is not a very common problem, as it would require the template
|
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.
|
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
|
one security issue and also contains an opt-in feature to enhance the security
|
||||||
of :mod:`django.contrib.sessions`.
|
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
|
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
|
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
|
roots. For example, if ``ALLOWED_INCLUDE_ROOTS = ("/var/www",)`` the following
|
||||||
would be possible:
|
would be possible:
|
||||||
|
@ -22,8 +22,8 @@ would be possible:
|
||||||
{% ssi "/var/www/../../etc/passwd" %}
|
{% ssi "/var/www/../../etc/passwd" %}
|
||||||
|
|
||||||
In practice this is not a very common problem, as it would require the template
|
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
|
||||||
possible in principle.
|
in principle.
|
||||||
|
|
||||||
Mitigating a remote-code execution vulnerability in :mod:`django.contrib.sessions`
|
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
|
Django 1.3 introduced ``{% load ssi from future %}`` and
|
||||||
``{% load url from future %}`` syntax for forward compatibility of the
|
``{% 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
|
will be removed in Django 1.9. You can simply remove the
|
||||||
``{% load ... from future %}`` tags.
|
``{% load ... from future %}`` tags.
|
||||||
|
|
||||||
|
|
|
@ -1654,7 +1654,7 @@ Django 1.10.
|
||||||
``ssi`` template tag
|
``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
|
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
|
the :ttag:`include` tag often makes more sense. This tag is now deprecated and
|
||||||
will be removed in Django 1.10.
|
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
|
``DjangoTemplates`` engines accept the following :setting:`OPTIONS
|
||||||
<TEMPLATES-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
|
* ``'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.
|
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
|
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.
|
ImproperlyConfigured if they are set to a string instead of a list or tuple.
|
||||||
"""
|
"""
|
||||||
list_or_tuple_settings = (
|
list_or_tuple_settings = (
|
||||||
"ALLOWED_INCLUDE_ROOTS",
|
|
||||||
"INSTALLED_APPS",
|
"INSTALLED_APPS",
|
||||||
"TEMPLATE_DIRS",
|
"TEMPLATE_DIRS",
|
||||||
"LOCALE_PATHS",
|
"LOCALE_PATHS",
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
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', {})
|
libraries = getattr(self, 'libraries', {})
|
||||||
|
|
||||||
self.engine = Engine(
|
self.engine = Engine(
|
||||||
allowed_include_roots=[ROOT],
|
|
||||||
libraries=libraries,
|
libraries=libraries,
|
||||||
loaders=loaders,
|
loaders=loaders,
|
||||||
)
|
)
|
||||||
|
@ -65,7 +64,6 @@ def setup(templates, *args, **kwargs):
|
||||||
func(self)
|
func(self)
|
||||||
|
|
||||||
self.engine = Engine(
|
self.engine = Engine(
|
||||||
allowed_include_roots=[ROOT],
|
|
||||||
libraries=libraries,
|
libraries=libraries,
|
||||||
loaders=loaders,
|
loaders=loaders,
|
||||||
string_if_invalid='INVALID',
|
string_if_invalid='INVALID',
|
||||||
|
@ -74,7 +72,6 @@ def setup(templates, *args, **kwargs):
|
||||||
func(self)
|
func(self)
|
||||||
|
|
||||||
self.engine = Engine(
|
self.engine = Engine(
|
||||||
allowed_include_roots=[ROOT],
|
|
||||||
debug=True,
|
debug=True,
|
||||||
libraries=libraries,
|
libraries=libraries,
|
||||||
loaders=loaders,
|
loaders=loaders,
|
||||||
|
|
Loading…
Reference in New Issue