Skip to content

Commit

Permalink
fixup! mozillaGH-36 Update docs for multi-policy support
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanYoung committed May 26, 2022
1 parent 0b1b649 commit 1cc55a3
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 54 deletions.
25 changes: 20 additions & 5 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,18 @@ you may need to tweak here.
It's worth reading the latest CSP spec_ and making sure you understand it
before configuring django-csp.

Multiple policies can be configured using the below settings. There are two
reasons to do this:

1. To configure one policy as report-only and another to be enforced.

2. To have multiple policies enforced simultaneously for a directive, e.g
a ``{'include_nonce_in': ['default-src']}`` and ``{'default-src': ['self']}``.

Multiple policies for the same header will be separated by a ``,`` in the header.

.. note::
Many settings require a ``tuple`` or ``list``. You may get very strange
Many directives require a ``tuple`` or ``list``. You may get very strange
policies and even errors when mistakenly configuring them as a ``string``.


Expand All @@ -32,15 +42,20 @@ These settings affect the policy in the header. The defaults are in *italics*.
`default_policy` uses the defaults for each directive as shown in :ref:`deprecated-policy-settings`
and :ref:`deprecated-pseudo-directives` below.

The policy directives are lower-case and use dashes (``-``) rather than (``_``) used by the old
settings, with the exception of the :ref:`deprecated-pseudo-directives` (``report_only``,
``exclude_url_prefixes``, and ``include_nonce_in``) which are specified with underscores rather
than dashes to distinguish them visually from the csp directives.
The policy directives are lower-case and use dashes (``-``) rather than (``_``) used by the
:ref:`old settings<deprecated-policy-settings>`, with the exception of the
:ref:`deprecated-pseudo-directives` (``report_only``, ``exclude_url_prefixes``, and
``include_nonce_in``) which are specified with underscores rather than dashes to distinguish
them visually from the csp directives and for forwards compatibility.

``CSP_POLICIES``
A list or tuple specifying which definitions will be applied by default and
defining an order on those policies. *['default']*

Note that not all policies defined in ``CSP_POLICY_DEFINITIONS`` need to be used here. Those that
aren't can be selected for a particular view using the ``@csp_select``
:ref:`decorator <csp-select-decorator>`.


.. _deprecated-policy-settings:

Expand Down
181 changes: 132 additions & 49 deletions docs/decorators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,123 +9,206 @@ You may, on some views, need to expand or change the policy. django-csp
includes four decorators to help.


.. _csp-exempt-decorator:

``@csp_exempt``
===============

Using the ``@csp_exempt`` decorator disables the CSP header on a given
Using the ``@csp_exempt`` decorator disables the CSP headers on a given
view.

::

from csp.decorators import csp_exempt

# Will not have a CSP header.
# Will not have CSP headers.
@csp_exempt
def myview(request):
return render(...)

You can manually set this on a per-response basis by setting the
``_csp_exempt`` attribute on the response to ``True``::

# Also will not have a CSP header.
# Also will not have CSP headers.
def myview(request):
response = render(...)
response._csp_exempt = True
return response


``@csp_update``
.. _csp-select-decorator:

``@csp_select``
===============

The ``@csp_update`` header allows you to **append** values to the source
lists specified in the settings. If there is no setting, the value
passed to the decorator will be used verbatim.
The ``@csp_select`` decorator allows you to select policies to include
from the current policy definitions, including those added
through the ``@csp`` :ref:`decorator <csp-decorator>` or the
``@csp_append`` :ref:`decorator <csp-append-decorator>` and those
defined in ``CSP_POLICY_DEFINITIONS``.

.. note::
To quote the CSP spec: "There's no inheritance; ... the default list
is not used for that resource type" if it is set. E.g., the following
will not allow images from 'self'::
The arguments are positional-only names of policies.

It effectively overrides the ``CSP_POLICIES`` setting for a single view.
::

from csp.decorators import csp_select

# Will first apply the default policy and the alt second policy.
@csp_select('default', 'first')
@csp({
'alt': {
'default-src': ["'self'"],
'img-src': ['imgsrv.com'],
'report-only': True,
},
})
def myview(request):
return render(...)

default-src 'self'; img-src imgsrv.com

The arguments to the decorator the same as the :ref:`settings
<configuration-chapter>` without the ``CSP_`` prefix, e.g. ``IMG_SRC``.
(They are also case-insensitive.) The values are either strings, lists
or tuples.
.. _csp-update-decorator:

``@csp_update``
===============

The ``@csp_update`` decorator allows you to **append** values to the source
lists specified in a policy. If there is no setting, the value
passed to the decorator will be used verbatim. There are two different
parameter formats:

1. Keyword arguments that are the uppercased CSP directives, with dashes
replaced by underscores (the same as the :ref:`deprecated-policy-settings`
without the ``CSP_`` prefix, but case-insensitive). The values are either
strings, lists or tuples. In this mode of calling there is an optional
positional argument specifying which named policies to which to apply the
directives (default: ``('default',)``).
::

from csp.decorators import csp_update

# Will allow images from imgsrv.com.
# Will allow images from imgsrv.com in the default policy.
@csp_update(IMG_SRC='imgsrv.com')
def myview(request):
return render(...)

2. Keyword arguments that are named policies equivalent to the format of
``CSP_POLICY_DEFINITIONS``.
::

from csp.decorators import csp_update

# Will allow images from imgsrv.com in the default policy.
@csp_update(default={'img-src': 'imgsrv.com'})
def myview(request):
return render(...)

.. note::
To quote the CSP spec: "There's no inheritance; ... the default list
is not used for that resource type" if it is set. E.g., the following
will not allow images from 'self'::

default-src 'self'; img-src imgsrv.com


.. _csp-replace-decorator:


``@csp_replace``
================

The ``@csp_replace`` decorator allows you to **replace** a source list
specified in settings. If there is no setting, the value passed to the
decorator will be used verbatim. (See the note under ``@csp_update``.)
specified in a policy. Setting a directive to ``None`` will delete that
directive from the policy. If there is no setting, the value passed to the
decorator will be used verbatim. See the note under
``@csp_update`` :ref:`decorator <csp-update-decorator>`.

The arguments and values are the same as ``@csp_update``
::

from csp.decorators import csp_replace

# settings.CSP_IMG_SRC = ['imgsrv.com']
# settings.CSP_POLICY_DEFINITIONS = {'default': {'img-src': 'imgsrv.com'}}
# Will allow images from imgsrv2.com, but not imgsrv.com.
@csp_replace(IMG_SRC='imgsrv2.com')
def myview(request):
return render(...)

# OR

``@csp_select``
===============
@csp_replace(default={'img-src': 'imgsrv2.com'})
def myview(request):
return render(...)

The ``@csp_select`` decorator allows you to select policies to include
from the current policy definitions being applied.

It accepts a mixed iterable of names or indices into the compiled definitions.
.. _csp-decorator:

It acts very much like the ``CSP_POLICIES`` setting except that it can use
indices, which don't work for ``CSP_POLICIES`` because it's used to define
the order on the compiled policy in the first place).
``@csp``
========

If you need to replace the entire policy list on a view, ignoring all the
settings, you can use the ``@csp`` decorator.

NOTE: in the case of passing a ``dict`` to one of the other decorators,
the order will not be well-defined before Python 3.6.
Avoid using indices in this cases.
The ``@csp_select`` :ref:`decorator <csp-select-decorator>` can be used to
combine these with the policies configured in ``CSP_POLICY_DEFINITIONS``
(but see also the ``@csp_append`` :ref:`decorator <csp-append-decorator>` below).

For named policies it will fallback to ``CSP_POLICY_DEFINITIONS`` even if they
don't appear in the current policy, so use with care
The arguments and values are the same as the ``@csp_update``
:ref:`decorator <csp-update-decorator>` except that it accepts optional position
arguments that are unnamed policies.
::

from csp.decorators import csp_select
from csp.decorators import csp

# Using default settings
# Will first apply the default policy, then the second policy, then the first policy
@csp_select(['default', 1, 0]) # or @csp_select(['default', 'second', 'first])
@csp(csp_definitions=(
('first', {'default-src': ["'self'"], 'img-src': ['imgsrv.com']}),
('second', {'script-src': ['scriptsrv.com', 'googleanalytics.com']},
))
@csp(
DEFAULT_SRC=["'self'"],
IMG_SRC=['imgsrv.com'],
SCRIPT_SRC=['scriptsrv.com', 'googleanalytics.com'],
)
def myview(request):
return render(...)

# OR

``@csp``
========
@csp(new={
default-src=["'self'"],
img-src=['imgsrv.com'],
script-src=['scriptsrv.com', 'googleanalytics.com'],
})
def myview(request):
return render(...)

# OR

If you need to set the entire policy on a view, ignoring all the
settings, you can use the ``@csp`` decorator. The arguments and values
are as above
@csp({
default-src=["'self'"],
img-src=['imgsrv.com'],
script-src=['scriptsrv.com', 'googleanalytics.com'],
})
def myview(request):
return render(...)


.. _csp-append-decorator:

``@csp_append``
===============

The ``@csp_append`` decorator allows you to add a new policy to
the policies configured in settings to a view.

The arguments and values are the same as the ``@csp``
:ref:`decorator <csp-decorator>`.
::

from csp.decorators import csp
from csp.decorators import csp_append

@csp(DEFAULT_SRC=["'self'"], IMG_SRC=['imgsrv.com'],
SCRIPT_SRC=['scriptsrv.com', 'googleanalytics.com'])
# Add this stricter policy as report_only for myview.
@csp_append({
default-src=["'self'"],
img-src=['imgsrv.com'],
script-src=['scriptsrv.com', 'googleanalytics.com'],
report_only=True,
})
def myview(request):
return render(...)

0 comments on commit 1cc55a3

Please sign in to comment.