""" TSZ (Thyris Safe Zone) - Interactive Demo ========================================== GitHub: https://github.com/thyrisAI/safe-zone Website: https://thyris.ai """ import gradio as gr import re import uuid import hashlib from dataclasses import dataclass from typing import List, Tuple @dataclass class Detection: type: str value: str placeholder: str start: int end: int confidence: str mask_id: str class TSZDetector: PATTERNS = { "EMAIL": { "pattern": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', "description": "Email addresses" }, "PHONE": { "pattern": r'\b(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}\b', "description": "Phone numbers (US format)" }, "PHONE_TR": { "pattern": r'\b(?:\+90|0)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{3}[-.\s]?[0-9]{2}[-.\s]?[0-9]{2}\b', "description": "Phone numbers (Turkey format)" }, "CREDIT_CARD": { "pattern": r'\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\b', "description": "Credit card numbers" }, "SSN": { "pattern": r'\b[0-9]{3}-[0-9]{2}-[0-9]{4}\b', "description": "Social Security Numbers (US)" }, "TC_KIMLIK": { "pattern": r'\b[1-9][0-9]{10}\b', "description": "Turkish National ID" }, "IBAN": { "pattern": r'\b[A-Z]{2}[0-9]{2}[A-Z0-9]{4}[0-9]{7}([A-Z0-9]?){0,16}\b', "description": "International Bank Account Numbers" }, "IP_ADDRESS": { "pattern": r'\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b', "description": "IPv4 addresses" }, "API_KEY": { "pattern": r'\b(?:sk-[a-zA-Z0-9]{32,}|api[_-]?key[_-]?[=:]\s*["\']?[a-zA-Z0-9]{16,}["\']?)\b', "description": "API keys and secrets" }, "AWS_KEY": { "pattern": r'\b(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}\b', "description": "AWS Access Keys" }, "PASSWORD": { "pattern": r'(?:password|passwd|pwd)[_\s]*[=:]\s*["\']?[^\s"\']{4,}["\']?', "description": "Passwords in config/code" }, "JWT_TOKEN": { "pattern": r'\beyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\b', "description": "JSON Web Tokens" } } def _generate_mask_id(self, value: str) -> str: """Generate a short unique mask ID based on value hash""" hash_val = hashlib.md5(value.encode()).hexdigest()[:6] return hash_val def detect(self, text: str, rid: str = "NO-RID") -> Tuple[List[Detection], str]: detections = [] redacted_text = text type_counters = {} for pii_type, config in self.PATTERNS.items(): pattern = config["pattern"] for match in re.finditer(pattern, text, re.IGNORECASE): value = match.group() if pii_type == "TC_KIMLIK" and len(value) != 11: continue if pii_type not in type_counters: type_counters[pii_type] = 0 type_counters[pii_type] += 1 mask_id = self._generate_mask_id(value) placeholder = f"[{rid}_{pii_type}_{mask_id}]" detections.append(Detection( type=pii_type, value=value, placeholder=placeholder, start=match.start(), end=match.end(), confidence="HIGH" if len(value) > 8 else "MEDIUM", mask_id=mask_id )) detections.sort(key=lambda x: x.start, reverse=True) for det in detections: redacted_text = redacted_text[:det.start] + det.placeholder + redacted_text[det.end:] detections.sort(key=lambda x: x.start) return detections, redacted_text detector = TSZDetector() def analyze_text(text: str, rid: str) -> Tuple[str, str, str, str]: if not text.strip(): return "", "Please enter some text to analyze.", "", "" if not rid.strip(): rid = f"TSZ-{uuid.uuid4().hex[:8].upper()}" detections, redacted_text = detector.detect(text, rid) if not detections: return text, "No sensitive information detected.", "**0** PII entities found", f"Request ID: `{rid}`" report_lines = ["### Detected Entities\n"] by_type = {} for det in detections: if det.type not in by_type: by_type[det.type] = [] by_type[det.type].append(det) for pii_type, items in by_type.items(): desc = detector.PATTERNS[pii_type]["description"] report_lines.append(f"\n**{pii_type}** ({desc})") for item in items: masked_value = item.value[:3] + "***" + item.value[-2:] if len(item.value) > 5 else "***" report_lines.append(f"- `{masked_value}` β†’ `{item.placeholder}`") report_lines.append(f" - Mask ID: `{item.mask_id}` | Confidence: {item.confidence}") report = "\n".join(report_lines) stats = f"**{len(detections)}** PII entities found across **{len(by_type)}** categories" rid_display = f"Request ID: `{rid}`" return redacted_text, report, stats, rid_display EXAMPLES = [ ["Hi, my name is John Smith and my email is john.smith@company.com. You can reach me at +1-555-123-4567. My SSN is 123-45-6789.", "REQ-001"], ["Customer Order #12345\nName: Jane Doe\nEmail: jane.doe@example.org\nPhone: (555) 987-6543\nCredit Card: 4532015112830366", "ORDER-12345"], ["API Configuration:\napi_key = \"sk-1234567890abcdefghijklmnopqrstuvwxyz\"\nAWS_ACCESS_KEY: AKIAIOSFODNN7EXAMPLE\npassword: \"super_secret_123\"", "CONFIG-SCAN"], ["Merhaba, ben Ahmet Yilmaz. TC Kimlik numaram 12345678901.\nTelefon: 0532 123 45 67\nIBAN: TR330006100519786457841326", "TR-REQ-001"], ] with gr.Blocks(title="TSZ - Thyris Safe Zone Demo", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # πŸ›‘οΈ TSZ (Thyris Safe Zone) **Enterprise-grade PII detection and guardrails gateway** that prevents sensitive data from leaking to LLMs and third-party APIs. [![GitHub](https://img.shields.io/badge/GitHub-Repository-black?logo=github)](https://github.com/thyrisAI/safe-zone) [![Website](https://img.shields.io/badge/Website-thyris.ai-blue)](https://thyris.ai) [![License](https://img.shields.io/badge/License-Apache%202.0-green)](https://github.com/thyrisAI/safe-zone/blob/main/LICENSE) """) with gr.Tabs(): with gr.TabItem("πŸ” Interactive Demo"): with gr.Row(): with gr.Column(): rid_input = gr.Textbox( label="Request ID (RID)", placeholder="e.g., REQ-001 (auto-generated if empty)", lines=1 ) input_text = gr.Textbox( label="Input Text", placeholder="Paste text containing emails, phone numbers, credit cards, API keys...", lines=8 ) analyze_btn = gr.Button("Analyze & Redact", variant="primary") gr.Markdown("### Try These Examples") gr.Examples(examples=EXAMPLES, inputs=[input_text, rid_input]) with gr.Column(): rid_display = gr.Markdown(label="Request ID") stats_output = gr.Markdown(label="Statistics") redacted_output = gr.Textbox( label="Redacted Output (Safe to send to LLMs)", lines=8, interactive=False ) detection_report = gr.Markdown(label="Detection Report") gr.Markdown(""" --- ### Placeholder Format TSZ uses the format `[RID_TYPE_maskId]` for placeholders: - **RID**: Request ID for audit correlation - **TYPE**: Detection type (EMAIL, PHONE, etc.) - **maskId**: Unique identifier derived from the original value Example: `[REQ-001_EMAIL_a1b2c3]` This allows you to: - Track which request generated the redaction - Identify the type of sensitive data - Correlate with audit logs for compliance """) analyze_btn.click(fn=analyze_text, inputs=[input_text, rid_input], outputs=[redacted_output, detection_report, stats_output, rid_display]) input_text.submit(fn=analyze_text, inputs=[input_text, rid_input], outputs=[redacted_output, detection_report, stats_output, rid_display]) with gr.TabItem("πŸ“š API Reference"): gr.Markdown(""" ## TSZ API Reference TSZ is an enterprise-grade PII detection and guardrails gateway. It acts as a zero-trust middleware between your applications and external systems. --- ### Confidence & Guardrails Model | Confidence | Action | |------------|--------| | < 0.30 | ALLOW (ignored) | | 0.30 – 0.85 | MASK (redact) | | β‰₯ 0.85 | AUTO-BLOCK | --- ### POST /detect **Request:** ```json { "text": "My email is user@company.com", "rid": "REQ-001", "guardrails": ["TOXIC_LANGUAGE"] } ``` **Response:** ```json { "redacted_text": "My email is [REQ-001_EMAIL_a1b2c3]", "detections": [ { "type": "EMAIL", "value": "user@company.com", "placeholder": "[REQ-001_EMAIL_a1b2c3]", "start": 12, "end": 28, "confidence_score": "0.78", "confidence_explanation": { "source": "HYBRID", "regex_score": "0.55", "ai_score": "0.90", "final_score": "0.78" } } ], "blocked": false, "contains_pii": true, "overall_confidence": "0.78" } ``` --- ### Placeholder Format TSZ generates unique placeholders in the format: ``` [{RID}_{TYPE}_{maskId}] ``` - **RID**: Request ID for audit log correlation - **TYPE**: Detection type (EMAIL, CREDIT_CARD, etc.) - **maskId**: Hash-based unique identifier Example: `[RID-GW-001_EMAIL_a1b2c3]` --- ### POST /v1/chat/completions OpenAI-compatible LLM Gateway with built-in guardrails. **Headers:** - `X-TSZ-RID`: Request ID for audit logs - `X-TSZ-Guardrails`: Comma-separated validators - `X-TSZ-Guardrails-Mode`: `final-only` | `stream-sync` | `stream-async` **Response with tsz_meta:** ```json { "id": "chatcmpl-58", "choices": [...], "tsz_meta": { "rid": "RID-GW-001", "guardrails": ["TOXIC_LANGUAGE"], "input": [ { "redacted_text": "My email is [RID-GW-001_EMAIL_a1b2c3]", "detections": [...], "blocked": false } ], "output": [...] } } ``` --- ### Pattern Management | Method | Endpoint | Description | |--------|----------|-------------| | `POST` | `/patterns` | Create pattern | | `GET` | `/patterns` | List patterns | | `DELETE` | `/patterns/{id}` | Delete pattern | --- ### Validators & Guardrails ```json { "name": "TOXIC_LANGUAGE", "type": "AI_PROMPT", "rule": "Is this text toxic? Answer YES or NO.", "expected_response": "NO" } ``` --- ### Health & Admin | Endpoint | Description | |----------|-------------| | `GET /healthz` | Liveness probe | | `GET /ready` | Readiness probe | | `POST /admin/reload` | Clear caches | --- πŸ“– **Full Documentation:** [API_REFERENCE.md](https://github.com/thyrisAI/safe-zone/blob/main/docs/API_REFERENCE.md) """) with gr.TabItem("πŸš€ Quick Start"): gr.Markdown(""" ## Get Started with TSZ ### Docker (Recommended) ```bash git clone https://github.com/thyrisAI/safe-zone.git cd safe-zone docker-compose up -d ``` TSZ will be available at `http://localhost:8080` --- ### Environment Variables ```bash # LLM Gateway Configuration THYRIS_AI_MODEL_URL=https://api.openai.com/v1 THYRIS_AI_API_KEY=sk-your-key # Confidence Thresholds CONFIDENCE_ALLOW_THRESHOLD=0.30 CONFIDENCE_BLOCK_THRESHOLD=0.85 # PII Mode PII_MODE=MASK # or BLOCK GATEWAY_BLOCK_MODE=BLOCK # BLOCK, MASK, or WARN ``` --- ### Test with Request ID ```bash curl -X POST http://localhost:8080/detect \\ -H "Content-Type: application/json" \\ -d '{ "text": "Email: test@example.com", "rid": "MY-REQ-001" }' ``` **Response:** ```json { "redacted_text": "Email: [MY-REQ-001_EMAIL_abc123]", "detections": [...], "contains_pii": true } ``` --- ### Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Your App │────▢│ TSZ │────▢│ LLM API β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β” β”‚ β”‚ Audit Log β”‚ β”‚ β”‚ (with RID)β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Άβ””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- πŸ“– **Documentation:** [github.com/thyrisAI/safe-zone/docs](https://github.com/thyrisAI/safe-zone/tree/main/docs) """) with gr.TabItem("ℹ️ About"): gr.Markdown(""" ## About TSZ (Thyris Safe Zone) Developed by **Thyris.AI** as an open-source enterprise AI security solution. ### Key Features | Feature | Description | |---------|-------------| | **Hybrid Detection** | Regex + AI-powered confidence scoring | | **Traceable Redaction** | `[RID_TYPE_maskId]` format for audit | | **LLM Gateway** | OpenAI-compatible proxy | | **Streaming Support** | sync/async modes for SSE | | **Guardrails** | AI validators (toxic, schema, custom) | --- ### Placeholder System Each redacted value gets a unique, traceable placeholder: ``` [RID-GW-001_EMAIL_a1b2c3] β”‚ β”‚ β”‚ β”‚ β”‚ └── Unique mask ID (hash of value) β”‚ └── Detection type └── Request ID for correlation ``` Benefits: - Full audit trail - SIEM integration - Compliance reporting - Debug & investigation --- ### Compliance Ready - βœ… GDPR (EU) - βœ… KVKK (Turkey) - βœ… CCPA (California) - βœ… HIPAA (Healthcare) - βœ… PCI-DSS (Payment) --- Built with ❀️ by [Thyris.AI](https://thyris.ai) """) if __name__ == "__main__": demo.launch()