Skip to content

Commit e99af8f

Browse files
authored
Fix issue with FIPS compliance and CAT. (#148)
1 parent 9930373 commit e99af8f

File tree

5 files changed

+62
-20
lines changed

5 files changed

+62
-20
lines changed

newrelic/api/cat_header_mixin.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,21 @@ def generate_request_headers(cls, transaction):
8888

8989
elif settings.cross_application_tracer.enabled:
9090
transaction.is_part_of_cat = True
91-
encoded_cross_process_id = obfuscate(settings.cross_process_id,
92-
settings.encoding_key)
93-
nr_headers.append((cls.cat_id_key, encoded_cross_process_id))
94-
95-
transaction_data = [transaction.guid, transaction.record_tt,
96-
transaction.trip_id, transaction.path_hash]
97-
encoded_transaction = obfuscate(json_encode(transaction_data),
98-
settings.encoding_key)
99-
nr_headers.append(
100-
(cls.cat_transaction_key, encoded_transaction))
91+
path_hash = transaction.path_hash
92+
if path_hash is None:
93+
# Disable cat if path_hash fails to generate.
94+
transaction.is_part_of_cat = False
95+
else:
96+
encoded_cross_process_id = obfuscate(settings.cross_process_id,
97+
settings.encoding_key)
98+
nr_headers.append((cls.cat_id_key, encoded_cross_process_id))
99+
100+
transaction_data = [transaction.guid, transaction.record_tt,
101+
transaction.trip_id, path_hash]
102+
encoded_transaction = obfuscate(json_encode(transaction_data),
103+
settings.encoding_key)
104+
nr_headers.append(
105+
(cls.cat_transaction_key, encoded_transaction))
101106

102107
if transaction.synthetics_header:
103108
nr_headers.append(

newrelic/api/transaction.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,16 @@ def path_hash(self):
719719
except Exception:
720720
seed = 0
721721

722-
path_hash = generate_path_hash(identifier, seed)
722+
try:
723+
path_hash = generate_path_hash(identifier, seed)
724+
except ValueError:
725+
_logger.warning(
726+
"Unable to generate cross application tracer headers. "
727+
"MD5 hashing may not be available. (Is this system FIPS compliant?) "
728+
"We recommend enabling distributed tracing instead. For details and a transition guide see "
729+
"https://docs.newrelic.com/docs/agents/python-agent/configuration/python-agent-configuration#distributed-tracing-settings"
730+
)
731+
return None
723732

724733
# Only store up to 10 alternate path hashes.
725734

newrelic/common/encoding_utils.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@
1717
1818
"""
1919

20-
import random
21-
import itertools
22-
import types
2320
import base64
24-
import json
25-
import zlib
26-
import io
2721
import gzip
22+
import hashlib
23+
import io
24+
import itertools
25+
import json
26+
import random
2827
import re
28+
import types
29+
import zlib
2930
from collections import OrderedDict
30-
from hashlib import md5
3131

3232
from newrelic.packages import six
3333

@@ -265,7 +265,7 @@ def generate_path_hash(name, seed):
265265
if not isinstance(name, bytes):
266266
name = name.encode('UTF-8')
267267

268-
path_hash = (rotated ^ int(md5(name).hexdigest()[-8:], base=16))
268+
path_hash = (rotated ^ int(hashlib.md5(name).hexdigest()[-8:], base=16))
269269
return '%08x' % path_hash
270270

271271

tests/agent_features/test_cat.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
end style test, it does not fit as a unittest.
2222
"""
2323

24+
import pytest
2425
import webtest
2526

27+
from newrelic.api.background_task import background_task
28+
from newrelic.api.external_trace import ExternalTrace
2629
from newrelic.api.wsgi_application import wsgi_application
2730

2831
from testing_support.fixtures import (make_cross_agent_headers,
@@ -72,3 +75,28 @@ def test_cat_insertion_disabled_on_304():
7275
headers = make_cross_agent_headers(payload, ENCODING_KEY, '1#1')
7376
response = test_application.get('/304', headers=headers)
7477
assert 'X-NewRelic-App-Data' not in response.headers
78+
79+
_override_settings = {
80+
'cross_application_tracing.enabled': True,
81+
'distributed_tracing.enabled': False,
82+
}
83+
@override_application_settings(_override_settings)
84+
@pytest.mark.parametrize('fips_enabled', (False, True))
85+
@background_task()
86+
def test_cat_fips_compliance(monkeypatch, fips_enabled):
87+
# Set md5 to raise a ValueError to simulate FIPS compliance issues.
88+
def md5_crash(*args, **kwargs):
89+
raise ValueError()
90+
91+
if fips_enabled:
92+
# monkeypatch.setattr("hashlib.md5", md5_crash)
93+
import hashlib
94+
monkeypatch.setattr(hashlib, "md5", md5_crash)
95+
96+
# Generate and send request using actual transaction api instead of fixture.
97+
# Otherwise the proper code paths are not exercised.
98+
with ExternalTrace("cat_test", "http://localhost/200") as tracer:
99+
headers = tracer.generate_request_headers(tracer.transaction)
100+
101+
expected = not fips_enabled # Invert to make more human readable
102+
assert ('X-NewRelic-Transaction' in dict(headers)) == expected

tests/cross_agent/test_cat_map.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def run_cat_test():
197197

198198
content = response.html.html.body.p.string
199199

200-
# Validate actual body content as sansity check.
200+
# Validate actual body content as sanity check.
201201

202202
assert content == 'RESPONSE'
203203

0 commit comments

Comments
 (0)