Download app.py from ThyrisAI/TSZ: direct link, hf CLI and curl.
- Browser
- Download file 14.6 kB
-
https://huggingface.co/spaces/ThyrisAI/TSZ/resolve/main/app.py
- Command line
-
hf download hf://spaces/ThyrisAI/TSZ/app.py
-
curl -L -o app.py https://huggingface.co/spaces/ThyrisAI/TSZ/resolve/main/app.py
14.6 kB
| """ | |
| 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 | |
| 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. | |
| [](https://github.com/thyrisAI/safe-zone) | |
| [](https://thyris.ai) | |
| [](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() |