litellm/tests/test_litellm/proxy/guardrails
yucheng-berri e99151bb95
Some checks failed
GitHub Actions Security Analysis / zizmor (push) Waiting to run
LiteLLM Rust / rustfmt, clippy, test (push) Has been cancelled
feat(guardrails): make the Generic Guardrail resilient to built-in tools and errors (adopted from #31286) (#31461)
* fix(guardrails): stop Generic Guardrail API 500 on built-in tools

Requests carrying built-in tools (code_interpreter, file_search, ...) crashed
the Generic Guardrail with a 500. GenericGuardrailAPIRequest.tools validated
each tool against ChatCompletionToolParam, whose base TypedDict requires a
function block, so a tool like {"type": "code_interpreter"} raised a Pydantic
ValidationError before the request was ever sent.

Type the field with a permissive GuardrailToolParam model (type required,
extra=allow) so built-in tools validate and their config is forwarded to the
guardrail intact instead of being stripped.

* feat(guardrails): add complete fail-open (fail_on_error) to Generic Guardrail

The Generic Guardrail already honored unreachable_fallback, which fails open
only on network-unreachable errors. This wires up the existing generic
fail_on_error config (so far implemented only by Model Armor) so that
fail_on_error=false degrades any guardrail error to a critical-log warning and
lets the request proceed as if the guardrail were absent.

Only a valid guardrail response can act: a parsed BLOCKED decision still raises,
while endpoint errors, malformed responses, and internal serialization or
validation errors all fall through when fail_on_error=false. To cover that last
class, the request construction now runs inside the protected block, so the kind
of validation error that previously surfaced as a 500 is caught here too.

Defaults to true (fail closed), matching today's behavior; turning it off is an
explicit availability-over-security choice and is logged at critical level on
every bypass.

* test(guardrails): cover fail_on_error on the response path

The existing fail_on_error tests all drive the request path. Add response-path
(input_type=response) coverage: an endpoint error proceeds unchanged under
fail_on_error=false, and a valid BLOCKED decision still raises. Guards against a
future regression that special-cases input_type in the error handling.

* style(guardrails): black-format the fail-open guard expression

CI runs black (line-length 88) over litellm/; the unreachable_fail_open
assignment exceeded it. Wrap it to satisfy the formatter.

* fix(guardrails): validate tools into GuardrailToolParam at the call site

Changing the request field to List[GuardrailToolParam] left the construction
passing List[ChatCompletionToolParam] (list is invariant), which tripped the
basedpyright reportArgumentType budget gate. Validate each tool explicitly,
which is what Pydantic did implicitly, so the types line up with no Any or
suppression and the serialized payload is unchanged.

* fix(guardrails): make fail-open log message accurate for non-network errors

The fail-open path is now shared by fail_on_error, so it fires for any guardrail
error, not just unreachability. The log said 'unreachable' even for an HTTP 400
or a malformed response; reword to 'error' (the status code and exception are
already logged). Addresses the Greptile review's only finding.

* fix(guardrails): align GenericGuardrailAPIResponse.tools with GuardrailToolParam

Greptile flagged that the request side moved to GuardrailToolParam but the
response side still annotated tools as List[ChatCompletionToolParam], which
mandates a function block and contradicts the new built-in-tools support.
Update the response annotation (and the now-unused import) so the two sides
agree. Runtime is unchanged; from_dict stores the raw dicts and the only
consumer assigns through to GenericGuardrailAPIInputs without inspecting
the elements.

---------

Co-authored-by: Itay Ovadia <itay@sun.security>
2026-06-26 11:25:56 -07:00
..
guardrail_hooks feat(guardrails): make the Generic Guardrail resilient to built-in tools and errors (adopted from #31286) (#31461) 2026-06-26 11:25:56 -07:00
test_content_filter_path_traversal.py Litellm OSS Staging 010626 (#29422) 2026-06-01 21:42:51 -07:00
test_content_utils.py fix(guardrails): redact `data["input"]` for Responses-API mask paths 2026-05-01 04:41:57 +00:00
test_custom_code_security.py fix(guardrails): return HTTP 400 for litellm content filter blocks (#28418) 2026-05-30 14:28:46 -07:00
test_deferred_guardrail_logging.py feat(proxy): enforce key/team guardrails on bedrock passthrough routes (#30194) 2026-06-12 07:23:29 -07:00
test_guardrail_coverage.py fix(tests): use canonical litellm_enterprise import path (#27699) 2026-05-12 12:32:57 -07:00
test_guardrail_endpoints.py feat(guardrails): wire apply_guardrail into proxy logging callbacks (#28970) 2026-05-28 09:41:02 -07:00
test_guardrail_registry.py fix(guardrails): stop re-initializing DB guardrails on every poll (#30542) 2026-06-16 11:17:49 -07:00
test_init_guardrails.py Litellm fix GitHub action testing (#11163) 2025-05-26 14:41:42 -07:00
test_llm_as_a_judge.py feat(guardrails): LLM-as-a-Judge guardrail (#26360) 2026-04-24 17:15:32 -07:00
test_mcp_jwt_signer.py fix(mcp): JWT on tools/list and REST tools/call server resolution (#28227) 2026-05-20 13:31:44 -07:00
test_pillar_guardrails.py chore(proxy): harden request control fields 2026-04-29 22:35:17 -07:00
test_prompt_security_guardrails.py fixing prompt-security's guardrail implementation (#19374) 2026-01-21 20:09:40 -08:00
test_qostodian_nexus_guardrail.py feat: add Qohash Nexus guardrail hook (#24927) 2026-05-01 17:26:32 +05:30