mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-05 08:07:05 +00:00
fix(xecguard): sanitize scan result before recording it for logging (#32935)
This commit is contained in:
parent
f717e3b2f0
commit
f61fd2fb6d
2 changed files with 37 additions and 1 deletions
|
|
@ -44,6 +44,8 @@ from litellm.integrations.custom_guardrail import (
|
|||
CustomGuardrail,
|
||||
log_guardrail_information,
|
||||
)
|
||||
from litellm.litellm_core_utils.core_helpers import redact_nested_match_and_regex_keys
|
||||
from litellm.litellm_core_utils.sensitive_data_masker import mask_credentials_in_payload
|
||||
from litellm.llms.custom_httpx.http_handler import (
|
||||
get_async_httpx_client,
|
||||
httpxSpecialProvider,
|
||||
|
|
@ -64,6 +66,13 @@ if TYPE_CHECKING:
|
|||
)
|
||||
|
||||
|
||||
def _sanitize_scan_result_for_logging(scan_result: dict) -> dict:
|
||||
without_secrets = {key: value for key, value in scan_result.items() if key != "secret_fields"}
|
||||
redacted = redact_nested_match_and_regex_keys(without_secrets)
|
||||
masked = mask_credentials_in_payload(redacted if isinstance(redacted, dict) else without_secrets)
|
||||
return masked if isinstance(masked, dict) else without_secrets
|
||||
|
||||
|
||||
_DEFAULT_API_BASE = "https://api-xecguard.cycraft.ai"
|
||||
_SCAN_ENDPOINT = "/xecguard/v1/scan"
|
||||
_GROUNDING_ENDPOINT = "/xecguard/v1/grounding"
|
||||
|
|
@ -253,7 +262,7 @@ class XecGuardGuardrail(CustomGuardrail):
|
|||
slg = StandardLoggingGuardrailInformation(
|
||||
guardrail_name=self.guardrail_name or "xecguard",
|
||||
guardrail_mode=GuardrailEventHooks.logging_only,
|
||||
guardrail_response=scan_result,
|
||||
guardrail_response=_sanitize_scan_result_for_logging(scan_result),
|
||||
guardrail_status=guardrail_status,
|
||||
start_time=start_time.timestamp(),
|
||||
end_time=end_time.timestamp(),
|
||||
|
|
|
|||
|
|
@ -1675,6 +1675,33 @@ class TestXecGuardLoggingHook:
|
|||
assert info_list[1]["guardrail_name"] == "test-xecguard"
|
||||
assert info_list[1]["guardrail_response"]["trace_id"] == "lg-4"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_logging_hook_sanitizes_scan_result(
|
||||
self, xecguard_guardrail, mock_request_data
|
||||
):
|
||||
resp = _make_response(
|
||||
{
|
||||
"decision": "SAFE",
|
||||
"trace_id": "lg-5",
|
||||
"secret_fields": {"authorization": "Bearer xgs_raw"},
|
||||
"detections": [{"match": "raw matched span", "policy": "pii"}],
|
||||
"api_key": "xgs_super_secret_value",
|
||||
}
|
||||
)
|
||||
with patch.object(xecguard_guardrail.async_handler, "post", return_value=resp):
|
||||
kwargs = {**mock_request_data, "standard_logging_object": {}}
|
||||
await xecguard_guardrail.async_logging_hook(
|
||||
kwargs=kwargs,
|
||||
result=_build_model_response("some answer"),
|
||||
call_type="acompletion",
|
||||
)
|
||||
info = kwargs["standard_logging_object"]["guardrail_information"][0]
|
||||
guardrail_response = info["guardrail_response"]
|
||||
assert "secret_fields" not in guardrail_response
|
||||
assert guardrail_response["detections"][0]["match"] == "[REDACTED]"
|
||||
assert guardrail_response["api_key"] != "xgs_super_secret_value"
|
||||
assert guardrail_response["trace_id"] == "lg-5"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_logging_hook_without_response_records_info(
|
||||
self, xecguard_guardrail, mock_request_data
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue