Heray-Was-Here
Server : Apache
System : Linux dal-shared-66.hostwindsdns.com 4.18.0-513.24.1.lve.1.el8.x86_64 #1 SMP Thu May 9 15:10:09 UTC 2024 x86_64
User : krnuyqrm ( 1183)
PHP Version : 7.4.33
Disable Function : NONE
Directory :  /opt/cloudlinux/venv/lib/python3.11/site-packages/clwpos/user/website_check/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : //opt/cloudlinux/venv/lib/python3.11/site-packages/clwpos/user/website_check/errors.py
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2020 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT
import html
from urllib.parse import urlsplit

from clwpos import gettext as _
from clwpos.cl_wpos_exceptions import WposError


# Characters that could break out of the double-quoted ``href="%(url)s"``
# attribute or inject a tag in the anchor body. A url free of these is safe in
# the HTML anchor while remaining a valid plain-text value for the
# ``curl %(url)s`` fix_tip sink. A single quote ``'`` is intentionally NOT in
# this set: inside the double-quoted href attribute (and the anchor body) it is
# inert, yet it is a valid RFC-3986 sub-delim that legitimately appears in
# ``response.url`` (post-redirect target, checkers.py); rejecting it would blank
# a legitimate diagnostic url for no XSS gain.
_URL_BREAKING_CHARS = ('"', '<', '>')


def _safe_url(url):
    """Validate a caller-supplied url shared between the HTML templates and the
    plain-text ``curl %(url)s`` fix_tip.

    The same value feeds an HTML sink (the anchor in MESSAGE_TEMPLATE) and a
    plain-text sink (the curl fix_tip), so it must not be html-escaped: that
    would corrupt a legitimate url (e.g. ``?a=1&b=2`` -> ``?a=1&amp;b=2``) in
    the copy-pasteable command. Instead validate-and-reject: keep the url
    byte-for-byte when its scheme is http/https and it contains no
    attribute/tag-breaking characters; otherwise drop it to an empty string.
    A scheme-validated url with no breaking chars is safe in the HTML anchor
    (``&`` alone cannot break an attribute or inject script) and correct in the
    curl command.
    """
    if not isinstance(url, str):
        return ''
    # Reject raw whitespace / ASCII control bytes (CR, LF, tab, space, NUL,
    # DEL, ...) up front. A real http/https url percent-encodes these, so a
    # raw control byte is never a legitimate diagnostic url and must not land
    # verbatim in the href / the plain-text ``curl %(url)s`` tip. (A double-
    # quoted href attribute is not actually terminated by a newline -- only by
    # ``"`` -- but a url validator should not pass control characters through,
    # and it keeps urlsplit from having to cope with embedded controls.)
    if any(ch.isspace() or ord(ch) < 0x20 or ord(ch) == 0x7f for ch in url):
        return ''
    scheme = urlsplit(url).scheme.lower()
    if scheme not in ('http', 'https'):
        return ''
    if any(ch in url for ch in _URL_BREAKING_CHARS):
        return ''
    return url


# Context key whose value is a url shared between the HTML ``description`` and
# the plain-text ``curl %(url)s`` FIX_TIP. It must be validate-and-rejected
# (``_safe_url``) rather than html-escaped so the same byte-for-byte value stays
# correct in the copy-pasteable curl command while remaining safe in the anchor.
_URL_CONTEXT_KEY = 'url'


class WebsiteCheckError(WposError):
    """
    Generic base class for all website post checks.

    Every context value reaching the HTML ``description`` is neutralised here,
    centrally, so no subclass, future key, or direct base-class raise (e.g. the
    case-4 ``error_desc`` in wp_utils._process_post_check) can leak raw
    caller/site-influenced markup into the panel HTML sink. The ``url`` key is
    validate-and-rejected via ``_safe_url`` (kept raw-but-safe so it stays a
    valid ``curl %(url)s`` command in the plain-text FIX_TIP); every other value
    is html-escaped. ``self.context`` therefore holds values that are
    simultaneously safe in the HTML ``description`` and intact enough for the
    plain-text FIX_TIP (which only ever interpolates ``url``/``timeout``).
    """

    def __init__(self, *, header, description, fix_tip, context, details=None):
        super(WebsiteCheckError, self).__init__(
            message=description,
            context=self._neutralise_context(context)
        )
        self.header = header
        self.fix_tip = fix_tip
        self.details = details

    @staticmethod
    def _neutralise_context(context):
        """Return a copy of ``context`` safe for the HTML ``description`` sink:
        the ``url`` value validate-and-rejected via ``_safe_url`` (so the same
        value remains correct in the plain-text ``curl %(url)s`` FIX_TIP), and
        every other value html-escaped. Escaping is harmless on server-built
        values (paths, fixed ints render unchanged)."""
        if not context:
            return context
        return {
            key: _safe_url(value) if key == _URL_CONTEXT_KEY
            else html.escape(str(value), quote=True)
            for key, value in context.items()
        }


class PostCheckRequestException(WebsiteCheckError):
    HEADER = _('Http request failed')
    MESSAGE_TEMPLATE = _(
        'Fatal error on attempt to request website main page: '
        '<a href="%(url)s" target="_blank">%(url)s</a>. '
        'Error message: %(error)s')

    FIX_TIP = _(
        'Manually check website responds on http requests'
    )
    def __init__(self, url, error):
        super(PostCheckRequestException, self).__init__(
            header=self.HEADER,
            description=self.MESSAGE_TEMPLATE,
            context=dict(
                url=url,
                error=error
            ),
            fix_tip=self.FIX_TIP
        )


class WebsiteCheckBadHttpCode(WebsiteCheckError):
    """
    Happens when we get unexpected status code from website.
    Usually user must go check logs and fix this issue himself.
    """
    HEADER = _('Bad http status code response')
    MESSAGE_TEMPLATE = _(
        'Unsuccessful attempt to request website main page: '
        '<a href="%(url)s" target="_blank">%(url)s</a>. '
        'Webserver responded with http status code %(http_code)s.')

    FIX_TIP = _(
        'Search your webserver logs for errors and manually '
        'check that WordPress site is working properly.'
    )

    def __init__(self, url, http_code):
        super(WebsiteCheckBadHttpCode, self).__init__(
            header=self.HEADER,
            description=self.MESSAGE_TEMPLATE,
            context=dict(
                url=url,
                http_code=http_code
            ),
            fix_tip=self.FIX_TIP
        )


class WebsiteTimeout(WebsiteCheckError):
    """
    Happens when website does not respond in hardcoded timeout.
    """
    HEADER = _('Response timeout reached')
    MESSAGE_TEMPLATE = _(
        'Unsuccessful attempt to request website main page: '
        '<a href="%(url)s" target="_blank">%(url)s</a>. '
        'Webserver responds longer than %(timeout)s seconds '
        '(most likely it is not working).')

    FIX_TIP = _(
        'Make sure that WordPress site is working properly and '
        'specified url gives response in %(timeout)s seconds. '
        'Search your webserver logs for errors that may cause the timeout issue.'
    )

    def __init__(self, url, timeout):
        super(WebsiteTimeout, self).__init__(
            header=self.HEADER,
            description=self.MESSAGE_TEMPLATE,
            context=dict(
                url=url,
                timeout=timeout
            ),
            fix_tip=self.FIX_TIP
        )


class WebsiteHttpsBroken(WebsiteCheckError):
    """
    Happens when website has invalid
    or outdated https certificate.
    """
    HEADER = _('Broken https certificate')
    MESSAGE_TEMPLATE = _(
        'Unsuccessful attempt to request website main page: '
        '<a href="%(url)s" target="_blank">%(url)s</a>. '
        'Webserver redirected http request to https, but the last '
        'one does not have a valid certificate.')

    FIX_TIP = _(
        'Make sure that https certificate is valid and up to date or '
        'disable http to https redirection in your control panel.'
    )

    def __init__(self, url, details):
        super(WebsiteHttpsBroken, self).__init__(
            header=self.HEADER,
            description=self.MESSAGE_TEMPLATE,
            context=dict(
                url=url
            ),
            fix_tip=self.FIX_TIP,
            details=details
        )


class WebsiteNotResponding(WebsiteCheckError):
    """
    Happens when website does not have working webserver at all.
    """
    HEADER = _('Website it not responding')
    MESSAGE_TEMPLATE = _(
        'Unsuccessful attempt to request website main page: '
        '<a href="%(url)s" target="_blank">%(url)s</a>. '
        'Webserver did not return any response.')

    FIX_TIP = _(
        'Make sure that webserver is working and your website is accessible: '
        'you can use `curl %(url)s` to check response.'
    )

    def __init__(self, url, details):
        super(WebsiteNotResponding, self).__init__(
            header=self.HEADER,
            description=self.MESSAGE_TEMPLATE,
            context=dict(
                url=url
            ),
            fix_tip=self.FIX_TIP,
            details=details
        )


class RollbackException(WposError):

    MESSAGE_TEMPLATE = _(
        "Action failed and rolled back. Here is why."
    )

    def __init__(self, *reasons: WebsiteCheckError):
        self.errors = [
            dict(
                header=reason.header,
                description=reason.message,
                fix_tip=reason.fix_tip,
                type='post_check',
                context=reason.context,
                details=reason.details
            ) for reason in reasons
        ]
        super(RollbackException, self).__init__(message='rollback')


class PhpLogErrorsFound(WebsiteCheckError):
    HEADER = _('Website server error')
    MESSAGE_TEMPLATE = _(
        'We found some errors in the website log file:  %(error_log_path)s. '
        'Here is some context: \n'
        '%(log_record)s')

    FIX_TIP = _(
        'Check found errors and decide whether they are critical or not. '
        'Contact your system administrator for help if needed.'
    )

    def __init__(self, error_log_path, log_record):
        super(PhpLogErrorsFound, self).__init__(
            header=self.HEADER,
            description=self.MESSAGE_TEMPLATE,
            context=dict(
                error_log_path=error_log_path,
                log_record=log_record
            ),
            fix_tip=self.FIX_TIP
        )


class CDNActivationFailed(WebsiteCheckError):
    """
    Happens when website has invalid CDN configuration for some reason
    """
    HEADER = _('CDN malfunction')
    MESSAGE_TEMPLATE = _(
        "CDN feature was not activated properly. "
        "Changes were reverted and CDN module is now disabled.\n"
        "Event is logged to file: '%(logger_path)s' with stdout and stderr recorded.")

    FIX_TIP = _(
        'Activate feature again. If the issue persists, '
        'ignore error and check your website configuration '
        'with enabled CDN feature. '
        'Contact your hoster\'s support for help.'
    )

    def __init__(self, logger_path):
        super(CDNActivationFailed, self).__init__(
            header=self.HEADER,
            description=self.MESSAGE_TEMPLATE,
            fix_tip=self.FIX_TIP,
            context=dict(logger_path=logger_path)
        )


class JSCssCheckBadHttpCode(WebsiteCheckError):
    """
    Happens when we get unexpected status code from website.
    Usually user must go check logs and fix this issue himself.
    """
    HEADER = _('Bad http status code response')
    MESSAGE_TEMPLATE = _(
        'Unsuccessful attempt to request website styles and scripts. '
        'Webserver responded with http status code %(http_code)s.')

    FIX_TIP = _(
        'Search your webserver logs for errors and manually '
        'check that WordPress site is working properly.'
    )

    def __init__(self, http_code):
        super(JSCssCheckBadHttpCode, self).__init__(
            header=self.HEADER,
            description=self.MESSAGE_TEMPLATE,
            context=dict(
                http_code=http_code
            ),
            fix_tip=self.FIX_TIP
        )

Hry