Revision 0fa71fcc

b/snf-webproject/conf/10-snf-webproject-deploy.conf
17 17
## sets this header is in use.
18 18
#USE_X_FORWARDED_HOST = True
19 19
#
20
## Custom exception filter to 'cleanse' setting variables
21
#DEFAULT_EXCEPTION_REPORTER_FILTER = "synnefo.webproject.exception_filter.SynnefoExceptionReporterFilter"
20 22
## Settings / Cookies / Headers that should be 'cleansed'
21 23
#HIDDEN_SETTINGS = 'SECRET|PASSWORD|PROFANITIES_LIST|SIGNATURE|AMQP_HOSTS|'\
22 24
#                  'PRIVATE_KEY|DB_CONNECTION|TOKEN'
b/snf-webproject/synnefo/webproject/exception_filter.py
1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
from django.conf import settings
35
from django.views.debug import SafeExceptionReporterFilter
36
from django.http import HttpRequest, build_request_repr
37

  
38
HIDDEN_ALL = settings.HIDDEN_COOKIES + settings.HIDDEN_HEADERS
39
CLEANSED_SUBSTITUTE = u'********************'
40

  
41

  
42
class SynnefoExceptionReporterFilter(SafeExceptionReporterFilter):
43
    def is_active(self, request):
44
        # Ignore DEBUG setting. Always active filtering!
45
        return True
46

  
47
    def get_traceback_frame_variables(self, request, tb_frame):
48
        sensitive_variables = HIDDEN_ALL
49
        cleansed = []
50
        if self.is_active(request) and sensitive_variables:
51
            if sensitive_variables == '__ALL__':
52
                # Cleanse all variables
53
                for name, value in tb_frame.f_locals.items():
54
                    cleansed.append((name, CLEANSED_SUBSTITUTE))
55
                return cleansed
56
            else:
57
                # Cleanse specified variables
58
                for name, value in tb_frame.f_locals.items():
59
                    if name in sensitive_variables:
60
                        value = CLEANSED_SUBSTITUTE
61
                    elif isinstance(value, HttpRequest):
62
                        # Cleanse the request's POST parameters.
63
                        value = self.get_request_repr(value)
64
                    cleansed.append((name, value))
65
                return cleansed
66
        else:
67
            # Potentially cleanse only the request if it's one of the frame
68
            # variables.
69
            for name, value in tb_frame.f_locals.items():
70
                if isinstance(value, HttpRequest):
71
                    # Cleanse the request's POST parameters.
72
                    value = self.get_request_repr(value)
73
                cleansed.append((name, value))
74
            return cleansed
75

  
76
    def get_request_repr(self, request):
77
        if request is None:
78
            return repr(None)
79
        else:
80
            # Use custom method method to build the request representation
81
            # where all sensitive values will be cleansed
82
            _repr = self.build_request_repr(request)
83
            # Respect max mail size
84
            if len(_repr) > settings.MAIL_MAX_LEN:
85
                _repr += "Mail size over limit (truncated)\n\n" + _repr
86
            return _repr[:settings.MAIL_MAX_LEN]
87

  
88
    def build_request_repr(self, request):
89
        cleansed = {}
90
        for fields in ["GET", "POST", "COOKIES", "META"]:
91
            _cleansed = getattr(request, fields).copy()
92
            for key in _cleansed.keys():
93
                for hidden in HIDDEN_ALL:
94
                    if hidden in key:
95
                        _cleansed[key] = CLEANSED_SUBSTITUTE
96
            cleansed[fields] = _cleansed
97
        return build_request_repr(request,
98
                                  GET_override=cleansed["GET"],
99
                                  POST_override=cleansed["POST"],
100
                                  COOKIES_override=cleansed["COOKIES"],
101
                                  META_override=cleansed["META"])
b/snf-webproject/synnefo/webproject/middleware/__init__.py
1 1
from log import LoggingConfigMiddleware
2 2
from secure import SecureMiddleware
3 3
from remoteaddr import RemoteAddrMiddleware
4
from cleanse import CleanseSettingsMiddleware
/dev/null
1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
from django.conf import settings
35
from django.core.exceptions import MiddlewareNotUsed
36

  
37
from django.core import mail
38

  
39
HIDDEN_ALL = settings.HIDDEN_COOKIES + settings.HIDDEN_HEADERS
40

  
41

  
42
def mail_admins_safe(subject, message, fail_silently=False,
43
                     connection=None, html_message=None):
44
    '''
45
    Wrapper function to cleanse email body from sensitive content before
46
    sending it
47
    '''
48
    new_msg = ""
49

  
50
    if len(message) > settings.MAIL_MAX_LEN:
51
        new_msg += "Mail size over limit (truncated)\n\n"
52
        message = message[:settings.MAIL_MAX_LEN]
53

  
54
    for line in message.splitlines():
55
        # Lines of interest in the mail are in the form of
56
        # key:value.
57
        try:
58
            (key, value) = line.split(':', 1)
59
        except ValueError:
60
            new_msg += line + '\n'
61
            continue
62

  
63
        new_msg += key + ':'
64

  
65
        # Special case when the first header / cookie printed
66
        # (prefixed by 'META:{' or 'COOKIES:{') needs to be hidden.
67
        if value.startswith('{'):
68
            try:
69
                (newkey, newval) = value.split(':', 1)
70
            except ValueError:
71
                new_msg += value + '\n'
72
                continue
73

  
74
            new_msg += newkey + ':'
75
            key = newkey.lstrip('{')
76
            value = newval
77

  
78
        if key.strip(" '") not in HIDDEN_ALL:
79
            new_msg += value + '\n'
80
            continue
81

  
82
        # Append value[-1] to the clensed string, so that commas / closing
83
        # brackets are printed correctly.
84
        # (it will 'eat up' the closing bracket if the header is the last one
85
        # printed)
86
        new_msg += ' ' + '*'*8 + value[-1] + '\n'
87

  
88
    return mail.mail_admins_plain(subject, new_msg, fail_silently, connection)
89

  
90

  
91
class CleanseSettingsMiddleware(object):
92
    '''
93
    Prevent django from printing sensitive information (paswords, tokens
94
    etc), when handling server errors (for both DEBUG and no-DEBUG
95
    deployments.
96
    '''
97
    def __init__(self):
98
        if not hasattr(mail, 'mail_admins_plain'):
99
            mail.mail_admins_plain = mail.mail_admins
100
            mail.mail_admins = mail_admins_safe
101

  
102
        raise MiddlewareNotUsed('cleanse settings')
b/snf-webproject/synnefo/webproject/settings/__init__.py
65 65
    'django.middleware.common.CommonMiddleware',
66 66
    #'django.contrib.messages.middleware.MessageMiddleware',
67 67
    'synnefo.webproject.middleware.LoggingConfigMiddleware',
68
    'synnefo.webproject.middleware.CleanseSettingsMiddleware'
69 68
)
70 69
MIDDLEWARE_CLASSES = extend_list_from_entry_point(MIDDLEWARE_CLASSES, \
71 70
        'synnefo', 'web_middleware')
b/snf-webproject/synnefo/webproject/settings/default/deploy.py
17 17
# sets this header is in use.
18 18
USE_X_FORWARDED_HOST = True
19 19

  
20
# Custom exception filter to 'cleanse' setting variables
21
DEFAULT_EXCEPTION_REPORTER_FILTER = "synnefo.webproject.exception_filter.SynnefoExceptionReporterFilter"
20 22
# Settings / Cookies / Headers that should be 'cleansed'
21 23
HIDDEN_SETTINGS = 'SECRET|PASSWORD|PROFANITIES_LIST|SIGNATURE|AMQP_HOSTS|'\
22 24
                  'PRIVATE_KEY|DB_CONNECTION|TOKEN'

Also available in: Unified diff