diff --git a/.ds.baseline b/.ds.baseline index 69663da27..049c580e4 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -161,7 +161,7 @@ "filename": "app/config.py", "hashed_secret": "577a4c667e4af8682ca431857214b3a920883efc", "is_verified": false, - "line_number": 123, + "line_number": 121, "is_secret": false } ], @@ -634,5 +634,5 @@ } ] }, - "generated_at": "2025-07-22T17:07:31Z" + "generated_at": "2025-07-29T21:32:59Z" } diff --git a/app/__init__.py b/app/__init__.py index 024c7bff7..fe6ba515d 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -2,6 +2,7 @@ import os import pathlib import re import secrets +import sys from functools import partial from time import monotonic from urllib.parse import unquote, urlparse, urlunparse @@ -147,7 +148,6 @@ navigation = { def _csp(config): asset_domain = config["ASSET_DOMAIN"] - logo_domain = config["LOGO_CDN_DOMAIN"] api_public_url = config["API_PUBLIC_URL"] api_public_ws_url = config["API_PUBLIC_WS_URL"] @@ -179,7 +179,7 @@ def _csp(config): f"{api_public_ws_url}", ], "style-src": ["'self'", asset_domain], - "img-src": ["'self'", asset_domain, logo_domain], + "img-src": ["'self'", asset_domain], } @@ -545,26 +545,28 @@ def register_errorhandlers(application): # noqa (C901 too complex) @application.errorhandler(HTTPError) def render_http_error(error): + error_url = error.response.url if error.response else "unknown URL" + application.logger.warning( - "API {} failed with status {} message {}".format( - error.response.url if error.response else "unknown", - error.status_code, - error.message, - ) + f"API {error_url} failed with status {error.status_code} message {error.message}", + exc_info=sys.exc_info(), + stack_info=True ) + error_code = error.status_code + if error_code not in [401, 404, 403, 410]: # probably a 500 or 503. # it might be a 400, which we should handle as if it's an internal server error. If the API might # legitimately return a 400, we should handle that within the view or the client that calls it. application.logger.exception( - "API {} failed with status {} message {}".format( - error.response.url if error.response else "unknown", - error.status_code, - error.message, - ) + f"API {error_url} failed with status {error.status_code} message {error.message}", + exc_info=sys.exc_info(), + stack_info=True ) + error_code = 500 + return _error_response(error_code) @application.errorhandler(400) diff --git a/app/config.py b/app/config.py index 51aedfffa..48d966cd8 100644 --- a/app/config.py +++ b/app/config.py @@ -19,9 +19,7 @@ class Config(object): HEADER_COLOUR = ( "#81878b" # mix of dark-grey and mid-grey ) - LOGO_CDN_DOMAIN = ( - "static-logos.notifications.service.gov.uk" # TODO use our own CDN - ) + ASSETS_DEBUG = False # Credentials diff --git a/notifications_python_client/base.py b/notifications_python_client/base.py index 32f76808d..6312416ea 100644 --- a/notifications_python_client/base.py +++ b/notifications_python_client/base.py @@ -2,6 +2,8 @@ import json import logging import urllib.parse +from os import getenv + import requests from notifications_python_client import __version__ @@ -10,21 +12,23 @@ from notifications_python_client.errors import HTTPError, InvalidResponse logger = logging.getLogger(__name__) +API_PUBLIC_URL = getenv("API_PUBLIC_URL", "localhost") + class BaseAPIClient: """ - Base class for GOV.UK Notify API client. + Base class for Notify.gov API client. This class is not thread-safe. """ def __init__( - self, api_key, base_url="https://api.notifications.service.gov.uk", timeout=30 + self, api_key, base_url=API_PUBLIC_URL, timeout=30 ): """ Initialise the client Error if either of base_url or secret missing - :param base_url - base URL of GOV.UK Notify API: + :param base_url - base URL of Notify.gov API: :param secret - application secret - used to sign the request: :param timeout - request timeout on the client :return: diff --git a/tests/app/main/views/test_headers.py b/tests/app/main/views/test_headers.py index 5551d9035..33ef2c540 100644 --- a/tests/app/main/views/test_headers.py +++ b/tests/app/main/views/test_headers.py @@ -49,4 +49,4 @@ def test_owasp_useful_headers_set( expected_sources <= actual_sources ), f"Missing sources in connect-src: {expected_sources - actual_sources}" assert search(r"style-src 'self' static\.example\.com 'nonce-.*';", csp) - assert search(r"img-src 'self' static\.example\.com static-logos\.test\.com", csp) + assert search(r"img-src 'self' static\.example\.com", csp)