From 88692a38518a6adfa71b5dc8f5fcf171fdc39991 Mon Sep 17 00:00:00 2001 From: Manasjyoti Sharma Date: Mon, 17 Aug 2026 19:17:30 +0530 Subject: [PATCH] fix: normalize OTLP gRPC authorization metadata --- src/fortifyroot/_vendor/VENDOR_DEPENDENCIES.json | 2 +- src/fortifyroot/_vendor/VENDOR_MANIFEST.json | 8 ++++---- .../_vendor/tracer/sdk/exporters/headers.py | 11 +++++++++++ .../_vendor/tracer/sdk/logging/logging.py | 7 ++++++- .../_vendor/tracer/sdk/metrics/metrics.py | 7 ++++++- .../_vendor/tracer/sdk/tracing/tracing.py | 13 ++++++++++--- src/fortifyroot/core.py | 13 +++++++------ tests/test_init.py | 8 ++++---- 8 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 src/fortifyroot/_vendor/tracer/sdk/exporters/headers.py diff --git a/src/fortifyroot/_vendor/VENDOR_DEPENDENCIES.json b/src/fortifyroot/_vendor/VENDOR_DEPENDENCIES.json index f4a8222..b9c0927 100644 --- a/src/fortifyroot/_vendor/VENDOR_DEPENDENCIES.json +++ b/src/fortifyroot/_vendor/VENDOR_DEPENDENCIES.json @@ -83,7 +83,7 @@ "pytest": ">=8.2.2,<9", "pytest-asyncio": ">=0.23.7,<1.4.0", "pytest-recording": ">=0.13.1,<0.14.0", - "pytest-sugar": "==1.0.0", + "pytest-sugar": "==1.1.1", "requests": ">=2.31.0,<3", "sqlalchemy": ">=2.0.31,<3", "vcrpy": ">=8.0.0,<9" diff --git a/src/fortifyroot/_vendor/VENDOR_MANIFEST.json b/src/fortifyroot/_vendor/VENDOR_MANIFEST.json index 121c6f7..17e8281 100644 --- a/src/fortifyroot/_vendor/VENDOR_MANIFEST.json +++ b/src/fortifyroot/_vendor/VENDOR_MANIFEST.json @@ -1,10 +1,10 @@ { - "vendored_at": "2026-07-06T21:20:43.609893", + "vendored_at": "2026-08-17T19:10:07.715156", "openllmetry_version": "0.52.6", - "git_commit": "ea31a61a6518", + "git_commit": "55d7ea06823d", "git_branch": "HEAD", - "git_tag": "fr-v0.52.6.34", - "git_base_tag": "fr-v0.52.6.34", + "git_tag": "fr-v0.52.6.35", + "git_base_tag": "fr-v0.52.6.35", "git_dirty": false, "git_dirty_files": [], "instrumentation_package_policy": { diff --git a/src/fortifyroot/_vendor/tracer/sdk/exporters/headers.py b/src/fortifyroot/_vendor/tracer/sdk/exporters/headers.py new file mode 100644 index 0000000..c72dbca --- /dev/null +++ b/src/fortifyroot/_vendor/tracer/sdk/exporters/headers.py @@ -0,0 +1,11 @@ +from typing import Dict + + +def grpc_metadata_headers(headers: Dict[str, str]) -> Dict[str, str]: + """Normalize HTTP-style headers for gRPC metadata. + + gRPC metadata keys must be lowercase. The public SDK accepts conventional + HTTP casing (for example ``Authorization``), so every gRPC signal exporter + must normalize keys before passing them to grpcio. + """ + return {str(key).lower(): str(value) for key, value in headers.items()} diff --git a/src/fortifyroot/_vendor/tracer/sdk/logging/logging.py b/src/fortifyroot/_vendor/tracer/sdk/logging/logging.py index 3e7b63e..ae01996 100644 --- a/src/fortifyroot/_vendor/tracer/sdk/logging/logging.py +++ b/src/fortifyroot/_vendor/tracer/sdk/logging/logging.py @@ -17,6 +17,7 @@ FortifyRootGRPCLogExporter as GRPCExporter, FortifyRootHTTPLogExporter as HTTPExporter, ) +from fortifyroot._vendor.tracer.sdk.exporters.headers import grpc_metadata_headers LOCAL_EXPORT_HOSTS = {"localhost"} @@ -139,7 +140,11 @@ def init_logging_exporter(endpoint: str, headers: Dict[str, str]) -> LogExporter grpc_endpoint, insecure = _resolve_grpc_exporter_endpoint(trimmed_endpoint) return cast( LogExporter, - GRPCExporter(endpoint=grpc_endpoint, headers=headers, insecure=insecure), + GRPCExporter( + endpoint=grpc_endpoint, + headers=grpc_metadata_headers(headers), + insecure=insecure, + ), ) diff --git a/src/fortifyroot/_vendor/tracer/sdk/metrics/metrics.py b/src/fortifyroot/_vendor/tracer/sdk/metrics/metrics.py index ed98d16..684b7ee 100644 --- a/src/fortifyroot/_vendor/tracer/sdk/metrics/metrics.py +++ b/src/fortifyroot/_vendor/tracer/sdk/metrics/metrics.py @@ -21,6 +21,7 @@ FortifyRootGRPCMetricExporter as GRPCExporter, FortifyRootHTTPMetricExporter as HTTPExporter, ) +from fortifyroot._vendor.tracer.sdk.exporters.headers import grpc_metadata_headers LOCAL_EXPORT_HOSTS = {"localhost"} @@ -125,7 +126,11 @@ def init_metrics_exporter(endpoint: str, headers: Dict[str, str]) -> MetricExpor base_url = f"{base_url}/v1/metrics" return HTTPExporter(endpoint=base_url, headers=headers) grpc_endpoint, insecure = _resolve_grpc_exporter_endpoint(trimmed_endpoint) - return GRPCExporter(endpoint=grpc_endpoint, headers=headers, insecure=insecure) + return GRPCExporter( + endpoint=grpc_endpoint, + headers=grpc_metadata_headers(headers), + insecure=insecure, + ) def init_metrics_provider( diff --git a/src/fortifyroot/_vendor/tracer/sdk/tracing/tracing.py b/src/fortifyroot/_vendor/tracer/sdk/tracing/tracing.py index 8db71fc..b9ab6f6 100644 --- a/src/fortifyroot/_vendor/tracer/sdk/tracing/tracing.py +++ b/src/fortifyroot/_vendor/tracer/sdk/tracing/tracing.py @@ -34,6 +34,7 @@ FortifyRootGRPCSpanExporter as GRPCExporter, FortifyRootHTTPSpanExporter as HTTPExporter, ) +from fortifyroot._vendor.tracer.sdk.exporters.headers import grpc_metadata_headers from fortifyroot._vendor.tracer.sdk.tracing.content_allow_list import ContentAllowList from fortifyroot._vendor.tracer.sdk.utils import is_notebook from fortifyroot._vendor.tracer.sdk.utils.package_check import is_package_installed @@ -412,17 +413,23 @@ def init_spans_exporter(api_endpoint: str, headers: Dict[str, str]) -> SpanExpor case "grpc": grpc_endpoint, insecure = _resolve_grpc_exporter_endpoint(api_endpoint) return GRPCExporter( - endpoint=grpc_endpoint, headers=headers, insecure=insecure + endpoint=grpc_endpoint, + headers=grpc_metadata_headers(headers), + insecure=insecure, ) case "grpcs": grpc_endpoint, insecure = _resolve_grpc_exporter_endpoint(api_endpoint) return GRPCExporter( - endpoint=grpc_endpoint, headers=headers, insecure=insecure + endpoint=grpc_endpoint, + headers=grpc_metadata_headers(headers), + insecure=insecure, ) case _: grpc_endpoint, insecure = _resolve_grpc_exporter_endpoint(api_endpoint) return GRPCExporter( - endpoint=grpc_endpoint, headers=headers, insecure=insecure + endpoint=grpc_endpoint, + headers=grpc_metadata_headers(headers), + insecure=insecure, ) diff --git a/src/fortifyroot/core.py b/src/fortifyroot/core.py index 1112532..49f550f 100644 --- a/src/fortifyroot/core.py +++ b/src/fortifyroot/core.py @@ -24,6 +24,7 @@ from opentelemetry.propagators.textmap import TextMapPropagator from fortifyroot._vendor.tracer.sdk import Traceloop +from fortifyroot._vendor.tracer.sdk.exporters.headers import grpc_metadata_headers from fortifyroot._internal.constants import ( FORTIFYROOT_SDK_LANGUAGE, @@ -427,7 +428,7 @@ def _init_default_metrics_exporter( MetricExporter, GRPCMetricExporter( endpoint=grpc_endpoint, - headers=headers, + headers=grpc_metadata_headers(headers), insecure=insecure, preferred_temporality=preferred_temporality, ), @@ -442,7 +443,7 @@ def _init_default_metrics_exporter( MetricExporter, GRPCMetricExporter( endpoint=grpc_endpoint, - headers=headers, + headers=grpc_metadata_headers(headers), insecure=insecure, preferred_temporality=preferred_temporality, ), @@ -457,7 +458,7 @@ def _init_default_metrics_exporter( MetricExporter, GRPCMetricExporter( endpoint=grpc_endpoint, - headers=headers, + headers=grpc_metadata_headers(headers), insecure=insecure, preferred_temporality=preferred_temporality, ), @@ -495,7 +496,7 @@ def _init_default_logging_exporter( LogExporter, GRPCLogExporter( endpoint=grpc_endpoint, - headers=headers, + headers=grpc_metadata_headers(headers), insecure=insecure, ), ) @@ -509,7 +510,7 @@ def _init_default_logging_exporter( LogExporter, GRPCLogExporter( endpoint=grpc_endpoint, - headers=headers, + headers=grpc_metadata_headers(headers), insecure=insecure, ), ) @@ -523,7 +524,7 @@ def _init_default_logging_exporter( LogExporter, GRPCLogExporter( endpoint=grpc_endpoint, - headers=headers, + headers=grpc_metadata_headers(headers), insecure=insecure, ), ) diff --git a/tests/test_init.py b/tests/test_init.py index c029425..59de88b 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -712,7 +712,7 @@ def test_init_uses_grpc_metrics_exporter_for_grpc_metrics_endpoint(self): metric_exporter_cls.assert_called_once_with( endpoint="metrics.example.com:4317", - headers={"Authorization": "Bearer fr-key"}, + headers={"authorization": "Bearer fr-key"}, insecure=False, preferred_temporality=_EXPECTED_TEMPORALITY, ) @@ -755,7 +755,7 @@ def test_init_uses_grpc_logging_exporter_for_grpc_logging_endpoint(self): logging_exporter_cls.assert_called_once_with( endpoint="localhost:4317", - headers={"Authorization": "Bearer fr-key"}, + headers={"authorization": "Bearer fr-key"}, insecure=True, ) _, kwargs = traceloop_init_mock.call_args @@ -1131,7 +1131,7 @@ def test_local_grpc_scheme_creates_insecure_grpc_metrics_exporter(self): metric_exporter_cls.assert_called_once_with( endpoint="localhost:4317", - headers={"Authorization": "Bearer fr-key"}, + headers={"authorization": "Bearer fr-key"}, insecure=True, preferred_temporality=_EXPECTED_TEMPORALITY, ) @@ -1234,7 +1234,7 @@ def test_grpcs_scheme_creates_secure_grpc_logging_exporter(self): logging_exporter_cls.assert_called_once_with( endpoint="logs.example.com:4317", - headers={"Authorization": "Bearer fr-key"}, + headers={"authorization": "Bearer fr-key"}, insecure=False, )