예제 #1
0
def test_get_config_defaults(dummy_request, config_key, default_value):
    config = get_config(dummy_request.registry)
    keys = config_key.split('.')
    if len(keys) == 1:
        assert default_value == getattr(config, keys[0])
    else:
        assert default_value == getattr(getattr(config, keys[0]), keys[1])
예제 #2
0
def test_get_config_keys(dummy_request):
    config = get_config(dummy_request.registry)
    expected_keys = (
        'proto_header',
        'ignore_paths',
        # tweens
        'ssl_redirect',
        'hsts_support',
        'csp_coverage',
    )
    assert expected_keys == tuple(config._asdict().keys())
def tween(handler, registry):
    r"""Sets HTTP Strict Transport Security Header as configured.

    About details of HSTS, see below:

    * https://hstspreload.org/
    * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/\
        Strict-Transport-Security#Preloading_Strict_Transport_Security
    """
    config = get_config(registry)

    hsts_support = config.hsts_support

    ignore_paths = config.ignore_paths
    if hsts_support.ignore_paths:
        ignore_paths = hsts_support.ignore_paths

    proto_header = config.proto_header
    if hsts_support.proto_header:
        proto_header = hsts_support.proto_header

    tween_name = 'hsts_support'

    def _hsts_support_tween(req):
        if not hsts_support.enabled:
            return handler(req)

        # ignore
        if apply_path_filter(req, ignore_paths):
            logger.info('(%s) Ignore path %s', tween_name, req.path)
            return handler(req)

        criteria = build_criteria(req, proto_header=proto_header)
        secure = all(criteria)

        if not secure:
            # sets the header only for https
            logger.warning('(%s) Insecure request %s', tween_name, req.url)
            return handler(req)

        res = handler(req)

        if HEADER_KEY not in res.headers:
            # ignore if already exists
            res.headers[HEADER_KEY] = build_hsts_header(hsts_support)

        return res

    return _hsts_support_tween
def test_hsts_tween_with_ssl_request_plus_extra_header_check(
        mocker, dummy_request):
    from pyramid_secure_response import hsts_support
    mocker.spy(hsts_support, 'apply_path_filter')
    mocker.spy(hsts_support, 'build_criteria')

    from pyramid.response import Response
    from pyramid_secure_response.hsts_support import (
        apply_path_filter,
        build_criteria,
    )

    from pyramid_secure_response.util import get_config

    dummy_request.url = 'https://example.org/'
    dummy_request.headers['X-Forwarded-Proto'] = 'https'
    dummy_request.registry.settings = {
        'pyramid_secure_response.hsts_support.enabled': 'True',
        'pyramid_secure_response.hsts_support.max_age': '604800',  # 1 week
        'pyramid_secure_response.hsts_support.include_subdomains': 'True',
        'pyramid_secure_response.hsts_support.preload': 'True',
        'pyramid_secure_response.hsts_support.proto_header':
            'X-Forwarded-Proto',
        'pyramid_secure_response.hsts_support.ignore_paths': '\n',
    }

    handler_stub = mocker.stub(name='handler_stub')
    handler_stub.return_value = Response(status=200)
    hsts_support_tween = tween(handler_stub, dummy_request.registry)
    res = hsts_support_tween(dummy_request)

    # pylint: disable=no-member
    assert 1 == handler_stub.call_count

    assert 1 == apply_path_filter.call_count
    apply_path_filter.assert_called_once_with(dummy_request, tuple())

    assert 1 == build_criteria.call_count
    config = get_config(dummy_request.registry)
    build_criteria.assert_called_once_with(
        dummy_request, proto_header=config.hsts_support.proto_header)

    assert 'Strict-Transport-Security' in res.headers
    assert 'max-age=604800; includeSubDomains; preload' == \
        res.headers['Strict-Transport-Security']
예제 #5
0
def test_build_csp_header(directives, header, dummy_request):
    from pyramid_secure_response.util import get_config
    from pyramid_secure_response.csp_coverage import build_csp_header

    dummy_request.url = 'http://example.org/'
    settings = {
        'pyramid_secure_response.csp_coverage.enabled': 'True',
    }
    for key, value in directives.items():
        settings['pyramid_secure_response.csp_coverage.{:s}'.format(
            key)] = value

    dummy_request.registry.settings = settings
    config = get_config(dummy_request.registry)

    # NOTE:
    # [fetch]
    # - connect_src
    # - default_src
    # - font_src
    # - frame_src
    # - img_src
    # - manifest_src
    # - media_src
    # - object_src
    # - script_src
    # - style_src
    # - worker_src
    # [document]
    # - base_uri
    # - plugin_types
    # - sandbox
    # [navigation]
    # - form_action
    # - frame_ancestors
    # [reporting]
    # - report_uri
    # - report_to
    # [other]
    # - block_all_mixed_content
    # - referrer
    # - require_sri_for
    # - upgrade_insecure_requests
    assert header == build_csp_header(config.csp_coverage)
예제 #6
0
def tween(handler, registry):
    """Redirects insecure HTTP request as configured.

    This tween does not handle request if insecure request comes.
    """
    config = get_config(registry)

    ssl_redirect = config.ssl_redirect

    ignore_paths = config.ignore_paths
    if ssl_redirect.ignore_paths:
        ignore_paths = ssl_redirect.ignore_paths

    proto_header = config.proto_header
    if ssl_redirect.proto_header:
        proto_header = ssl_redirect.proto_header

    tween_name = 'ssl_redirect'

    def _ssl_redirect_tween(req):
        if not ssl_redirect.enabled:
            return handler(req)

        # ignore
        if apply_path_filter(req, ignore_paths):
            logger.info('(%s) Ignore path %s', tween_name, req.path)
            return handler(req)

        criteria = build_criteria(req, proto_header=proto_header)
        secure = all(criteria)

        if secure:
            return handler(req)

        logger.warning('(%s) Insecure request %s', tween_name, req.url)

        raise HTTPMovedPermanently(location='https://{:s}{:s}'.format(
            req.host, req.path))

    return _ssl_redirect_tween
def tween(handler, registry):
    r"""Sets Content Security Policy Header as configured.

    About details of CSP, see below:

    * https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
    * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/\
        Content-Security-Policy
    """
    config = get_config(registry)

    csp_coverage = config.csp_coverage

    ignore_paths = config.ignore_paths
    if csp_coverage.ignore_paths:
        ignore_paths = csp_coverage.ignore_paths

    tween_name = 'csp_coverage'

    def _csp_coverage_tween(req):
        if not config.csp_coverage.enabled:
            return handler(req)

        if apply_path_filter(req, ignore_paths):
            logger.info('(%s) Ignore path %s', tween_name, req.path)
            return handler(req)

        res = handler(req)
        if HEADER_KEY not in res.headers:
            # ignore if already exists
            header_value = build_csp_header(csp_coverage)
            if header_value:
                res.headers[HEADER_KEY] = header_value

        return res

    return _csp_coverage_tween