# StripLLM

**DOMPurify, but for LLM context windows.**

StripLLM is an open-source Python SDK that sanitizes LLM inputs and outputs in your existing pipeline — no infrastructure changes, no external API calls, sub-10ms latency.

```bash
pip install stripllm
```

---

## Quickstart

```python
from stripllm import StripLLM

strip = StripLLM()

# 1. Block prompt injection before it reaches the LLM
safe_input = strip.clean(user_message)

# 2. Redact PII — get a mapping back for rehydration
redacted, mapping = strip.redact("Email me at john@acme.com")
# redacted → "Email me at [EMAIL_1]"
# mapping  → {"[EMAIL_1]": "john@acme.com"}

# After LLM responds, restore originals in the output
response = strip.rehydrate(llm_response, mapping)

# 3. Validate LLM output — enforce schema, catch leaks & hallucinations
validated = strip.enforce(llm_response, schema="json")

# 4. Full conversation risk audit
report = strip.audit(conversation)
print(report.risk_score)   # → 0.12
print(report)              # → formatted findings + recommendations
```

---

## API Reference

### `StripLLM(threshold=0.3)`

Initialize the sanitizer. `threshold` controls how sensitive `clean()` is (0.0–1.0, lower = stricter).

---

### `strip.clean(text: str) → str`

Detects prompt injection, jailbreak attempts, and unicode tricks. Raises `InjectionDetectedError` if risk score meets or exceeds threshold.

**Detects:**
- Classic overrides: "ignore previous instructions", "forget your training"
- Role hijacking: DAN, "you are now", "act as an unrestricted AI"
- System prompt extraction attempts
- Zero-width character attacks
- Unicode homoglyph tricks (NFKC normalization)

```python
try:
    safe = strip.clean(user_input)
except InjectionDetectedError as e:
    print(f"Blocked. Risk score: {e.risk_score}")
```

Non-raising variant:
```python
result = strip.scan(text)
result.detected       # bool
result.risk_score     # float 0.0–1.0
result.matched_patterns  # list of matched signatures
```

---

### `strip.redact(text: str, entities=None) → (str, dict)`

Replaces PII with typed placeholders. Returns `(redacted_text, mapping)`.

**Supported entity types:**
| Type | Example | Placeholder |
|---|---|---|
| `EMAIL` | john@acme.com | `[EMAIL_1]` |
| `PHONE` | 415-555-1234 | `[PHONE_1]` |
| `SSN` | 123-45-6789 | `[SSN_1]` |
| `CREDIT_CARD` | 4111111111111111 | `[CREDIT_CARD_1]` |
| `IP_ADDRESS` | 192.168.1.1 | `[IP_ADDRESS_1]` |
| `PASSPORT` | A12345678 | `[PASSPORT_1]` |
| `URL` | https://internal.corp/secret | `[URL_1]` |

```python
# Target specific entity types only
redacted, mapping = strip.redact(text, entities=["EMAIL", "SSN"])

# Restore originals after LLM processing
final_response = strip.rehydrate(llm_output, mapping)
```

---

### `strip.enforce(text: str, schema=None, raise_on_error=True) → str`

Validates LLM output for safety and structural correctness.

```python
# Require valid JSON
validated = strip.enforce(response, schema="json")

# Require specific keys and types
validated = strip.enforce(response, schema={"status": str, "count": int})

# Check for leaks only (no schema)
validated = strip.enforce(response)
```

**Detects:**
- Invalid JSON (when schema specified)
- Missing or wrong-typed keys
- Leaked system prompt delimiters (`<system>`, `[INST]`, `<<SYS>>`, etc.)
- Suspicious / likely-hallucinated URLs

Non-raising variant:
```python
result = strip.validate(text, schema="json")
result.valid      # bool
result.errors     # list of error strings
result.warnings   # list of warning strings
result.output     # best-effort cleaned output
```

---

### `strip.audit(conversation: list) → AuditReport`

Full security audit of a multi-turn conversation.

```python
conversation = [
    {"role": "user", "content": "My email is alice@corp.com. Help me with my account."},
    {"role": "assistant", "content": "Sure, I can help with that."},
]

report = strip.audit(conversation)
print(report.risk_score)      # 0.1
print(report.findings)        # list of Finding objects
print(report.recommendations) # list of action strings
print(report)                 # formatted summary
```

**AuditReport fields:**
- `risk_score: float` — overall conversation risk (0.0–1.0)
- `findings: List[Finding]` — per-turn findings with severity, category, description
- `recommendations: List[str]` — actionable remediation steps
- `turn_count: int`

---

## Why StripLLM vs Alternatives?

| | **StripLLM** | Lakera Guard | Rebuff | DIY Regex |
|---|---|---|---|---|
| Local (no API calls) | ✅ | ❌ | ❌ | ✅ |
| Latency | <10ms | ~100ms | ~200ms | <1ms |
| PII rehydration | ✅ | ❌ | ❌ | ❌ |
| Output validation | ✅ | ❌ | ❌ | ❌ |
| Conversation audit | ✅ | ❌ | ❌ | ❌ |
| Open source | ✅ MIT | ❌ | ❌ | ✅ |
| Zero config | ✅ | ❌ | ❌ | ✅ |

---

## Use Cases

**Customer support bots** — prevent prompt extraction, redact customer PII before sending to LLM

**RAG pipelines** — sanitize retrieved documents before injecting into context window

**AI coding assistants** — validate generated code output, catch leaked credentials

**Healthcare / Finance** — HIPAA/PCI-DSS compliant PII handling with full audit trail

---

## Running Tests

```bash
pip install -e ".[dev]"
pytest
```

---

## License

MIT — see [LICENSE](LICENSE)

---

## Enterprise?

Need centralized LLM security across your entire organization? Check out [Context Firewall](https://contextfirewall.com) — a reverse proxy / API gateway that applies StripLLM-style protection to every LLM request in your stack, with a real-time dashboard, SOC 2 audit trail, and RBAC.
