Merge pull request #26821 from BerriAI/litellm_fix-guardrail-auth-check

fix(proxy/auth): tighten guardrail modification permission check
This commit is contained in:
ryan-crabbe-berri 2026-04-29 19:48:34 -07:00 committed by GitHub
commit b1b98c3fc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 28 additions and 1 deletions

View File

@ -374,7 +374,7 @@ def _guardrail_modification_check(
coerced = _coerce_to_dict(container)
if coerced is None:
return False
return any(coerced.get(key) for key in _GUARDRAIL_MODIFICATION_KEYS)
return any(key in coerced for key in _GUARDRAIL_MODIFICATION_KEYS)
# Check both metadata keys — callers can populate either depending on the
# endpoint. Cover the top-level too so root-level injection is rejected.

View File

@ -2078,6 +2078,33 @@ class TestGuardrailModificationCheck:
)
assert exc.value.status_code == 403
@pytest.mark.parametrize(
"key",
[
"guardrails",
"disable_global_guardrails",
"disable_global_guardrail",
"opted_out_global_guardrails",
],
)
@pytest.mark.parametrize("empty_value", [{}, [], "", 0, False])
def test_rejects_empty_value_modification(self, key, empty_value):
"""Regression: an explicitly-supplied empty/falsy value still expresses
intent to modify and must trigger the permission check. Truthiness-based
gating let callers bypass the check by sending e.g.
``metadata={"guardrails": {}}``, which downstream evaluation interpreted
as "disable all guardrails" while the auth layer treated it as no-op.
"""
from fastapi import HTTPException
with patch(
"litellm.proxy.guardrails.guardrail_helpers.can_modify_guardrails",
return_value=False,
):
with pytest.raises(HTTPException) as exc:
self._call({"metadata": {key: empty_value}})
assert exc.value.status_code == 403
def test_rejects_injection_via_litellm_metadata_key(self):
"""Caller can populate the OTHER metadata key; that must also 403."""
from fastapi import HTTPException