Skip to content

[Feat] Allow specifying PII Entities Config when using Presidio Guardrails - #10810

Merged
ishaan-jaff merged 13 commits into
mainfrom
litellm_presidio_add_entity_config
May 14, 2025
Merged

[Feat] Allow specifying PII Entities Config when using Presidio Guardrails#10810
ishaan-jaff merged 13 commits into
mainfrom
litellm_presidio_add_entity_config

Conversation

@ishaan-jaff

@ishaan-jaff ishaan-jaff commented May 13, 2025

Copy link
Copy Markdown
Contributor

[Feat] Allow specifying PII Entities Config when using Presidio Guardrails

Entity Type Configuration

You can configure specific entity types for PII detection and decide how to handle each entity type (mask or block).

Configure Entity Types in config.yaml

Define your guardrails with specific entity type configuration:

model_list:
  - model_name: gpt-3.5-turbo
    litellm_params:
      model: openai/gpt-3.5-turbo
      api_key: os.environ/OPENAI_API_KEY

guardrails:
  - guardrail_name: "presidio-mask-guard"
    litellm_params:
      guardrail: presidio
      mode: "pre_call"
      pii_entities_config:
        CREDIT_CARD: "MASK"  # Will mask credit card numbers
        EMAIL_ADDRESS: "MASK"  # Will mask email addresses
        
  - guardrail_name: "presidio-block-guard"
    litellm_params:
      guardrail: presidio
      mode: "pre_call"
      pii_entities_config:
        CREDIT_CARD: "BLOCK"  # Will block requests containing credit card numbers

Supported Entity Types

LiteLLM Supports all Presidio entity types. See the complete list of presidio entity types here.

Supported Actions

For each entity type, you can specify one of the following actions:

  • MASK: Replace the entity with a placeholder (e.g., <PERSON>)
  • BLOCK: Block the request entirely if this entity type is detected

Test request with Entity Type Configuration

When using the masking configuration, entities will be replaced with placeholders:

curl http://localhost:4000/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-1234" \
  -d '{
    "model": "gpt-3.5-turbo",
    "messages": [
      {"role": "user", "content": "My credit card is 4111-1111-1111-1111 and my email is test@example.com"}
    ],
    "guardrails": ["presidio-mask-guard"]
  }'

Example response with masked entities:

{
  "id": "chatcmpl-123abc",
  "choices": [
    {
      "message": {
        "content": "I can see you provided a <CREDIT_CARD> and an <EMAIL_ADDRESS>. For security reasons, I recommend not sharing this sensitive information.",
        "role": "assistant"
      },
      "index": 0,
      "finish_reason": "stop"
    }
  ],
  // ... other response fields
}

When using the blocking configuration, requests containing the configured entity types will be blocked completely with an exception:

curl http://localhost:4000/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-1234" \
  -d '{
    "model": "gpt-3.5-turbo",
    "messages": [
      {"role": "user", "content": "My credit card is 4111-1111-1111-1111"}
    ],
    "guardrails": ["presidio-block-guard"]
  }'

When running this request, the proxy will raise a BlockedPiiEntityError exception.

{
  "error": {
    "message": "Blocked PII entity detected: CREDIT_CARD by Guardrail: presidio-block-guard."
  }
}

The exception includes the entity type that was blocked (CREDIT_CARD in this case) and the guardrail name that caused the blocking.

Relevant issues

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • I have added a screenshot of my new test passing locally
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem

Type

🆕 New Feature
✅ Test

Changes

@vercel

vercel Bot commented May 13, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
litellm ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 14, 2025 0:21am


verbose_proxy_logger.debug(
"Making request to: %s with payload: %s",
analyze_url,

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information

This expression logs [sensitive data (secret)](1) as clear text. This expression logs [sensitive data (secret)](2) as clear text. This expression logs [sensitive data (secret)](3) as clear text. This expression logs [sensitive data (secret)](4) as clear text. This expression logs [sensitive data (secret)](5) as clear text. This expression logs [sensitive data (secret)](6) as clear text. This expression logs [sensitive data (secret)](7) as clear text. This expression logs [sensitive data (secret)](8) as clear text. This expression logs [sensitive data (secret)](9) as clear text. This expression logs [sensitive data (secret)](10) as clear text. This expression logs [sensitive data (secret)](11) as clear text. This expression logs [sensitive data (secret)](12) as clear text. This expression logs [sensitive data (secret)](13) as clear text. This expression logs [sensitive data (secret)](14) as clear text. This expression logs [sensitive data (secret)](15) as clear text. This expression logs [sensitive data (secret)](16) as clear text. This expression logs [sensitive data (secret)](17) as clear text.

Copilot Autofix

AI over 1 year ago

To address the issue, sensitive data should be sanitized or excluded from logs. Specifically:

  1. Avoid logging the full analyze_url if it contains sensitive information. Instead, log only non-sensitive parts (e.g., the endpoint path).
  2. If the analyze_payload contains sensitive data, avoid logging it entirely or sanitize it before logging.

The fix involves modifying the verbose_proxy_logger.debug statement to exclude or sanitize sensitive information. For example:

  • Extract and log only the endpoint path from analyze_url.
  • Replace sensitive fields in analyze_payload with placeholders before logging.

Suggested changeset 1
litellm/proxy/guardrails/guardrail_hooks/presidio.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/litellm/proxy/guardrails/guardrail_hooks/presidio.py b/litellm/proxy/guardrails/guardrail_hooks/presidio.py
--- a/litellm/proxy/guardrails/guardrail_hooks/presidio.py
+++ b/litellm/proxy/guardrails/guardrail_hooks/presidio.py
@@ -207,6 +207,8 @@
 
+                sanitized_url = analyze_url.split("?")[0]  # Remove query parameters if present
+                sanitized_payload = {key: "[REDACTED]" if key.lower() in ["api_key", "token"] else value for key, value in analyze_payload.dict().items()}
                 verbose_proxy_logger.debug(
-                    "Making request to: %s with payload: %s",
-                    analyze_url,
-                    analyze_payload,
+                    "Making request to: %s with sanitized payload: %s",
+                    sanitized_url,
+                    sanitized_payload,
                 )
EOF
@@ -207,6 +207,8 @@

sanitized_url = analyze_url.split("?")[0] # Remove query parameters if present
sanitized_payload = {key: "[REDACTED]" if key.lower() in ["api_key", "token"] else value for key, value in analyze_payload.dict().items()}
verbose_proxy_logger.debug(
"Making request to: %s with payload: %s",
analyze_url,
analyze_payload,
"Making request to: %s with sanitized payload: %s",
sanitized_url,
sanitized_payload,
)
Copilot is powered by AI and may make mistakes. Always verify output.
async with aiohttp.ClientSession() as session:
# Make the request to /anonymize
anonymize_url = f"{self.presidio_anonymizer_api_base}anonymize"
verbose_proxy_logger.debug("Making request to: %s", anonymize_url)

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information

This expression logs [sensitive data (secret)](1) as clear text. This expression logs [sensitive data (secret)](2) as clear text. This expression logs [sensitive data (secret)](3) as clear text. This expression logs [sensitive data (secret)](4) as clear text. This expression logs [sensitive data (secret)](5) as clear text. This expression logs [sensitive data (secret)](6) as clear text. This expression logs [sensitive data (secret)](7) as clear text. This expression logs [sensitive data (secret)](8) as clear text. This expression logs [sensitive data (secret)](9) as clear text. This expression logs [sensitive data (secret)](10) as clear text. This expression logs [sensitive data (secret)](11) as clear text. This expression logs [sensitive data (secret)](12) as clear text. This expression logs [sensitive data (secret)](13) as clear text. This expression logs [sensitive data (secret)](14) as clear text. This expression logs [sensitive data (secret)](15) as clear text. This expression logs [sensitive data (secret)](16) as clear text. This expression logs [sensitive data (secret)](17) as clear text.

Copilot Autofix

AI over 1 year ago

To fix the issue, we should avoid logging sensitive information like the anonymize_url directly. Instead, we can log a sanitized or generic message that does not expose sensitive details. For example, we can log a message indicating that a request is being made to the anonymizer endpoint without including the full URL. This ensures that debugging information is still available without compromising security.

Changes required:

  1. Replace the verbose_proxy_logger.debug statement on line 234 with a sanitized log message that does not include the anonymize_url.
  2. Ensure that no sensitive data is logged elsewhere in the function.

Suggested changeset 1
litellm/proxy/guardrails/guardrail_hooks/presidio.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/litellm/proxy/guardrails/guardrail_hooks/presidio.py b/litellm/proxy/guardrails/guardrail_hooks/presidio.py
--- a/litellm/proxy/guardrails/guardrail_hooks/presidio.py
+++ b/litellm/proxy/guardrails/guardrail_hooks/presidio.py
@@ -233,3 +233,3 @@
                 anonymize_url = f"{self.presidio_anonymizer_api_base}anonymize"
-                verbose_proxy_logger.debug("Making request to: %s", anonymize_url)
+                verbose_proxy_logger.debug("Making request to the Presidio anonymizer endpoint.")
                 anonymize_payload = {
EOF
@@ -233,3 +233,3 @@
anonymize_url = f"{self.presidio_anonymizer_api_base}anonymize"
verbose_proxy_logger.debug("Making request to: %s", anonymize_url)
verbose_proxy_logger.debug("Making request to the Presidio anonymizer endpoint.")
anonymize_payload = {
Copilot is powered by AI and may make mistakes. Always verify output.
@ishaan-jaff
ishaan-jaff merged commit 8142c20 into main May 14, 2025
@ishaan-berri
ishaan-berri deleted the litellm_presidio_add_entity_config branch March 26, 2026 21:54
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
…rails (BerriAI#10810)

* refactor: use analyze_text, anonymize_text

* feat: allow defining pii_entities_config for presidio

* feat: use entities config for presidio analyze request

* feat: add test_presidio_pii.py

* testing: add guardrails testing job

* feat: allow blocking specific entities pii

* test: use 1 file for presidio guard tests

* fix: presidio pii tests

* test: presidio blocked entity

* clean up docs

* docs presidio pii parsing

* fix: raise_exception_if_blocked_entities_detected

* fix: linting errors
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants