Coverage for cc_modules/cc_response.py : 62%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
3"""
4camcops_server/cc_modules/cc_response.py
6===============================================================================
8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com).
10 This file is part of CamCOPS.
12 CamCOPS is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
17 CamCOPS is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
25===============================================================================
27**Implements a Pyramid Response object customized for CamCOPS.**
29"""
31from typing import TYPE_CHECKING
33from pyramid.response import Response
35from camcops_server.cc_modules.cc_baseconstants import (
36 DEFORM_SUPPORTS_CSP_NONCE,
37)
39if TYPE_CHECKING:
40 from camcops_server.cc_modules.cc_request import CamcopsRequest
43class CamcopsResponse(Response):
44 """
45 Response class, inheriting from Pyramid's response.
47 We do this mainly to set the HTTP ``Content-Security-Policy`` header to
48 match the nonce set by the
49 :class:``camcops_server.cc_modules.cc_request.CamcopsRequest``.
51 However, once this class exists, it may as well set all the standard
52 headers, rather than using additional middleware.
53 """
54 def __init__(self, camcops_request: "CamcopsRequest", **kwargs) -> None:
55 super().__init__(**kwargs)
56 nonce = camcops_request.nonce
57 self.headers.update([
58 # List of key, value tuples:
60 # -----------------------------------------------------------------
61 # Cache-Control: Caching
62 # -----------------------------------------------------------------
63 # NOT THIS:
64 # ("Cache-Control", "no-cache, no-store, must-revalidate"),
65 # ... or we get a ZAP error "Incomplete or No Cache-control and
66 # Pragma HTTP Header Set"
67 # ... note that Pragma is HTTP/1.0 and cache-control is HTTP/1.1,
68 # so you don't have to do both.
69 # BUT that prevents caching of images (e.g. logos), and we don't
70 # want that.
71 #
72 # The Pyramid @view_config decorator (or add_view function) takes
73 # an "http_cache" parameter, as per
74 # https://pyramid-pt-br.readthedocs.io/en/latest/api/config.html#pyramid.config.Configurator.add_view # noqa
75 # and it looks like viewderivers.py implements this. The default
76 # does not set the HTTP "cache-control" header, explained at
77 # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control # noqa
78 # The basic options are:
79 # - 0: do not cache
80 # - integer_seconds, or datetime.timedelta: lifespan
81 # - tuple (lifespan, dictionary_of_extra_cache_control_details)
82 # - tuple (None, dictionary_of_extra_cache_control_details)
83 # We now set http_cache for all our views via view_config, as well
84 # as the equivalent of using cache_max_age for add_static_view().
85 #
86 # However, this (as an additional Cache-Control header -- as well
87 # as any "cache, it's static" or "don't cache" header) sorts out
88 # any ZAP complaints:
90 ("Cache-Control", 'no-cache="Set-Cookie, Set-Cookie2"'),
92 # -----------------------------------------------------------------
93 # Content-Security-Policy: Control resources that are permitted to
94 # load, to mitigate against cross-site scripting attacks
95 # -----------------------------------------------------------------
96 # - Content-Security-Policy.
97 # - Recommended by Falanx penetration testing.
98 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy # noqa
99 # - Defaults from https://owasp.org/www-project-secure-headers/
100 # - Re scripts: see https://csper.io/blog/no-more-unsafe-inline
101 # - Re nonces (and in general): see
102 # https://stackoverflow.com/questions/42922784/what-s-the-purpose-of-the-html-nonce-attribute-for-script-and-style-elements # noqa
103 # - Note that multiple CSP headers combine to produce the most
104 # restrictive and can get confusing; best to use one. See
105 # https://chrisguitarguy.com/2019/07/05/working-with-multiple-content-security-policy-headers/ # noqa
106 (
107 "Content-Security-Policy",
108 # A single string:
109 (
110 # The secure policy:
112 "default-src 'self' data:; "
113 "object-src 'none'; "
114 "child-src 'self'; "
116 f"style-src 'nonce-{nonce}' 'self'; "
117 # ... meaning: allow inline CSS only if it is tagged with
118 # this nonce, via <style nonce="XXX">, or if it comes from
119 # our site ('self'). See
120 # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src # noqa
122 # And similarly for scripts:
123 f"script-src 'nonce-{nonce}' 'self'; "
124 # ... "unsafe-eval" is currently required by deform.js, in
125 # addSequenceItem(). Deform stores prototype code and then
126 # clones it when you add a sequence item; this involves
127 # evaluation.
129 "frame-ancestors 'none'; "
130 "upgrade-insecure-requests; "
131 "block-all-mixed-content"
132 ) if DEFORM_SUPPORTS_CSP_NONCE else (
133 # The less secure policy, for Deform:
135 "default-src 'self' data:; "
136 "object-src 'none'; "
137 "child-src 'self'; "
139 "style-src 'self' 'unsafe-inline'; "
140 "script-src 'self' 'unsafe-inline' 'unsafe-eval'; "
142 "frame-ancestors 'none'; "
143 "upgrade-insecure-requests; "
144 "block-all-mixed-content"
145 )
146 ),
148 # -----------------------------------------------------------------
149 # Strict-Transport-Security: Enforce HTTPS through the client.
150 # -----------------------------------------------------------------
151 # - In part this is by e.g. telling Google (and thus Chrome) that
152 # your site always uses HTTPS, to prevent HTTP-based spoofing.
153 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security # noqa
154 # - Advice is at
155 # https://blog.qualys.com/vulnerabilities-research/2016/03/28/the-importance-of-a-proper-http-strict-transport-security-implementation-on-your-web-server # noqa
157 ("Strict-Transport-Security", "max-age=31536000"), # = 1 year
159 # -----------------------------------------------------------------
160 # X-Content-Type-Options: Opt out of MIME type sniffing
161 # -----------------------------------------------------------------
162 # - Recommended by ZAP penetration testing.
163 # ... otherwise, the error is "X-Content-Type-Options Header
164 # Missing"
165 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # noqa
167 ("X-Content-Type-Options", "nosniff"),
169 # -----------------------------------------------------------------
170 # X-Frame-Options: Prevent rendering within a frame
171 # -----------------------------------------------------------------
172 # - Recommended by ZAP penetration testing.
173 # ... otherwise, the error is "X-Frame-Options Header Not Set"
174 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # noqa
176 ("X-Frame-Options", "DENY"),
178 # -----------------------------------------------------------------
179 # X-XSS-Protection: Check for cross-site scripting attacks
180 # -----------------------------------------------------------------
181 # - Recommended by Falanx penetration testing.
182 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # noqa
184 ("X-XSS-Protection", "1"),
186 ])
189def camcops_response_factory(request: "CamcopsRequest") -> Response:
190 """
191 Factory function to make a response object.
192 """
193 return CamcopsResponse(camcops_request=request)