From d27a53bc692e1384d1eecae20aa0e12435e05683 Mon Sep 17 00:00:00 2001 From: Tim Pansino Date: Thu, 7 Dec 2023 16:03:54 -0800 Subject: [PATCH 1/3] Add nonce to CSP in browser agent --- newrelic/api/transaction.py | 8 ++++---- newrelic/api/web_transaction.py | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/newrelic/api/transaction.py b/newrelic/api/transaction.py index bbe8f38522..3accbdeb7f 100644 --- a/newrelic/api/transaction.py +++ b/newrelic/api/transaction.py @@ -1904,17 +1904,17 @@ def add_framework_info(name, version=None): transaction.add_framework_info(name, version) -def get_browser_timing_header(): +def get_browser_timing_header(nonce=None): transaction = current_transaction() if transaction and hasattr(transaction, "browser_timing_header"): - return transaction.browser_timing_header() + return transaction.browser_timing_header(nonce) return "" -def get_browser_timing_footer(): +def get_browser_timing_footer(nonce=None): transaction = current_transaction() if transaction and hasattr(transaction, "browser_timing_footer"): - return transaction.browser_timing_footer() + return transaction.browser_timing_footer(nonce) return "" diff --git a/newrelic/api/web_transaction.py b/newrelic/api/web_transaction.py index 47155425e1..d4b4c58b87 100644 --- a/newrelic/api/web_transaction.py +++ b/newrelic/api/web_transaction.py @@ -39,8 +39,8 @@ _logger = logging.getLogger(__name__) -_js_agent_header_fragment = '' -_js_agent_footer_fragment = '' +_js_agent_footer_fragment = ''\ 'window.NREUM||(NREUM={});NREUM.info=%s' # Seconds since epoch for Jan 1 2000 @@ -156,6 +156,13 @@ def _is_websocket(environ): return environ.get('HTTP_UPGRADE', '').lower() == 'websocket' +def _encode_nonce(nonce): + if not nonce: + return "" + else: + return ' nonce="%s"' % ensure_str(nonce) # Extra space intentional + + class WebTransaction(Transaction): unicode_error_reported = False QUEUE_TIME_HEADERS = ('x-request-start', 'x-queue-start') @@ -386,7 +393,7 @@ def _update_agent_attributes(self): return super(WebTransaction, self)._update_agent_attributes() - def browser_timing_header(self): + def browser_timing_header(self, nonce=None): """Returns the JavaScript header to be included in any HTML response to perform real user monitoring. This function returns the header as a native Python string. In Python 2 native strings @@ -437,7 +444,7 @@ def browser_timing_header(self): # 'none'. if self._settings.js_agent_loader: - header = _js_agent_header_fragment % self._settings.js_agent_loader + header = _js_agent_header_fragment % (_encode_nonce(nonce), self._settings.js_agent_loader) # To avoid any issues with browser encodings, we will make sure # that the javascript we inject for the browser agent is ASCII @@ -476,7 +483,7 @@ def browser_timing_header(self): return header - def browser_timing_footer(self): + def browser_timing_footer(self, nonce=None): """Returns the JavaScript footer to be included in any HTML response to perform real user monitoring. This function returns the footer as a native Python string. In Python 2 native strings @@ -541,7 +548,7 @@ def browser_timing_footer(self): attributes = obfuscate(json_encode(attributes), obfuscation_key) footer_data['atts'] = attributes - footer = _js_agent_footer_fragment % json_encode(footer_data) + footer = _js_agent_footer_fragment % (_encode_nonce(nonce), json_encode(footer_data)) # To avoid any issues with browser encodings, we will make sure that # the javascript we inject for the browser agent is ASCII encodable. From 56bb621418af98f1079f25ec179ebdda6f4ab248 Mon Sep 17 00:00:00 2001 From: Tim Pansino Date: Thu, 7 Dec 2023 16:32:34 -0800 Subject: [PATCH 2/3] Adjust nonce position --- newrelic/api/web_transaction.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/newrelic/api/web_transaction.py b/newrelic/api/web_transaction.py index d4b4c58b87..d74f1d6d35 100644 --- a/newrelic/api/web_transaction.py +++ b/newrelic/api/web_transaction.py @@ -39,9 +39,8 @@ _logger = logging.getLogger(__name__) -_js_agent_header_fragment = '%s' -_js_agent_footer_fragment = ''\ - 'window.NREUM||(NREUM={});NREUM.info=%s' +_js_agent_header_fragment = '' +_js_agent_footer_fragment = '' # Seconds since epoch for Jan 1 2000 JAN_1_2000 = time.mktime((2000, 1, 1, 0, 0, 0, 0, 0, 0)) From 8e9e3efa9dcd6dbe4d097e7ec39439affab1e214 Mon Sep 17 00:00:00 2001 From: Tim Pansino Date: Thu, 7 Dec 2023 17:10:45 -0800 Subject: [PATCH 3/3] Add testing for browser timing nonces --- tests/agent_features/test_browser.py | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/agent_features/test_browser.py b/tests/agent_features/test_browser.py index e0f562d1e8..735cec5c12 100644 --- a/tests/agent_features/test_browser.py +++ b/tests/agent_features/test_browser.py @@ -32,6 +32,7 @@ get_browser_timing_footer, get_browser_timing_header, ) +from newrelic.api.web_transaction import web_transaction from newrelic.api.wsgi_application import wsgi_application from newrelic.common.encoding_utils import deobfuscate @@ -1026,3 +1027,31 @@ def test_html_insertion_manual_rum_insertion(): # footer added by the agent. response.mustcontain(no=["NREUM HEADER", "NREUM.info"]) + + +_test_get_browser_timing_nonces_settings = { + "browser_monitoring.enabled": True, + "browser_monitoring.auto_instrument": False, + "js_agent_loader": "", +} + +@override_application_settings(_test_get_browser_timing_nonces_settings) +@web_transaction(scheme="http", host="127.0.0.1", port=80, request_method="GET", + request_path="/", query_string=None, headers={}) +def test_get_browser_timing_nonces(): + header = get_browser_timing_header("NONCE") + footer = get_browser_timing_footer("NONCE") + + assert header == '' + assert '' + assert '