Alienseeker commited on
Commit
8a14f70
Β·
verified Β·
1 Parent(s): 2086529

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -195
app.py CHANGED
@@ -7,6 +7,8 @@ Website: https://thyris.ai
7
 
8
  import gradio as gr
9
  import re
 
 
10
  from dataclasses import dataclass
11
  from typing import List, Tuple
12
 
@@ -14,77 +16,73 @@ from typing import List, Tuple
14
  class Detection:
15
  type: str
16
  value: str
 
17
  start: int
18
  end: int
19
  confidence: str
 
20
 
21
  class TSZDetector:
22
  PATTERNS = {
23
  "EMAIL": {
24
  "pattern": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
25
- "placeholder": "[EMAIL]",
26
  "description": "Email addresses"
27
  },
28
  "PHONE": {
29
  "pattern": r'\b(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}\b',
30
- "placeholder": "[PHONE]",
31
  "description": "Phone numbers (US format)"
32
  },
33
  "PHONE_TR": {
34
  "pattern": r'\b(?:\+90|0)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{3}[-.\s]?[0-9]{2}[-.\s]?[0-9]{2}\b',
35
- "placeholder": "[PHONE]",
36
  "description": "Phone numbers (Turkey format)"
37
  },
38
  "CREDIT_CARD": {
39
  "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',
40
- "placeholder": "[CREDIT_CARD]",
41
  "description": "Credit card numbers"
42
  },
43
  "SSN": {
44
  "pattern": r'\b[0-9]{3}-[0-9]{2}-[0-9]{4}\b',
45
- "placeholder": "[SSN]",
46
  "description": "Social Security Numbers (US)"
47
  },
48
  "TC_KIMLIK": {
49
  "pattern": r'\b[1-9][0-9]{10}\b',
50
- "placeholder": "[TC_KIMLIK]",
51
  "description": "Turkish National ID"
52
  },
53
  "IBAN": {
54
  "pattern": r'\b[A-Z]{2}[0-9]{2}[A-Z0-9]{4}[0-9]{7}([A-Z0-9]?){0,16}\b',
55
- "placeholder": "[IBAN]",
56
  "description": "International Bank Account Numbers"
57
  },
58
  "IP_ADDRESS": {
59
  "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',
60
- "placeholder": "[IP_ADDRESS]",
61
  "description": "IPv4 addresses"
62
  },
63
  "API_KEY": {
64
  "pattern": r'\b(?:sk-[a-zA-Z0-9]{32,}|api[_-]?key[_-]?[=:]\s*["\']?[a-zA-Z0-9]{16,}["\']?)\b',
65
- "placeholder": "[API_KEY]",
66
  "description": "API keys and secrets"
67
  },
68
  "AWS_KEY": {
69
  "pattern": r'\b(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}\b',
70
- "placeholder": "[AWS_ACCESS_KEY]",
71
  "description": "AWS Access Keys"
72
  },
73
  "PASSWORD": {
74
  "pattern": r'(?:password|passwd|pwd)[_\s]*[=:]\s*["\']?[^\s"\']{4,}["\']?',
75
- "placeholder": "[PASSWORD]",
76
  "description": "Passwords in config/code"
77
  },
78
  "JWT_TOKEN": {
79
  "pattern": r'\beyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\b',
80
- "placeholder": "[JWT_TOKEN]",
81
  "description": "JSON Web Tokens"
82
  }
83
  }
84
 
85
- def detect(self, text: str) -> Tuple[List[Detection], str]:
 
 
 
 
 
86
  detections = []
87
  redacted_text = text
 
88
 
89
  for pii_type, config in self.PATTERNS.items():
90
  pattern = config["pattern"]
@@ -92,32 +90,51 @@ class TSZDetector:
92
  value = match.group()
93
  if pii_type == "TC_KIMLIK" and len(value) != 11:
94
  continue
 
 
 
 
 
 
 
 
 
 
 
95
  detections.append(Detection(
96
  type=pii_type,
97
  value=value,
 
98
  start=match.start(),
99
  end=match.end(),
100
- confidence="HIGH" if len(value) > 8 else "MEDIUM"
 
101
  ))
102
 
 
103
  detections.sort(key=lambda x: x.start, reverse=True)
104
  for det in detections:
105
- placeholder = self.PATTERNS[det.type]["placeholder"]
106
- redacted_text = redacted_text[:det.start] + placeholder + redacted_text[det.end:]
 
107
  detections.sort(key=lambda x: x.start)
108
 
109
  return detections, redacted_text
110
 
111
  detector = TSZDetector()
112
 
113
- def analyze_text(text: str) -> Tuple[str, str, str]:
114
  if not text.strip():
115
- return "", "Please enter some text to analyze.", ""
 
 
 
 
116
 
117
- detections, redacted_text = detector.detect(text)
118
 
119
  if not detections:
120
- return text, "No sensitive information detected.", "**0** PII entities found"
121
 
122
  report_lines = ["### Detected Entities\n"]
123
  by_type = {}
@@ -131,18 +148,20 @@ def analyze_text(text: str) -> Tuple[str, str, str]:
131
  report_lines.append(f"\n**{pii_type}** ({desc})")
132
  for item in items:
133
  masked_value = item.value[:3] + "***" + item.value[-2:] if len(item.value) > 5 else "***"
134
- report_lines.append(f"- `{masked_value}` (confidence: {item.confidence})")
 
135
 
136
  report = "\n".join(report_lines)
137
  stats = f"**{len(detections)}** PII entities found across **{len(by_type)}** categories"
 
138
 
139
- return redacted_text, report, stats
140
 
141
  EXAMPLES = [
142
- ["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."],
143
- ["Customer Order #12345\nName: Jane Doe\nEmail: jane.doe@example.org\nPhone: (555) 987-6543\nCredit Card: 4532015112830366"],
144
- ["API Configuration:\napi_key = \"sk-1234567890abcdefghijklmnopqrstuvwxyz\"\nAWS_ACCESS_KEY: AKIAIOSFODNN7EXAMPLE\npassword: \"super_secret_123\""],
145
- ["Merhaba, ben Ahmet Yilmaz. TC Kimlik numaram 12345678901.\nTelefon: 0532 123 45 67\nIBAN: TR330006100519786457841326"],
146
  ]
147
 
148
  with gr.Blocks(title="TSZ - Thyris Safe Zone Demo", theme=gr.themes.Soft()) as demo:
@@ -161,6 +180,11 @@ with gr.Blocks(title="TSZ - Thyris Safe Zone Demo", theme=gr.themes.Soft()) as d
161
  with gr.TabItem("πŸ” Interactive Demo"):
162
  with gr.Row():
163
  with gr.Column():
 
 
 
 
 
164
  input_text = gr.Textbox(
165
  label="Input Text",
166
  placeholder="Paste text containing emails, phone numbers, credit cards, API keys...",
@@ -168,9 +192,10 @@ with gr.Blocks(title="TSZ - Thyris Safe Zone Demo", theme=gr.themes.Soft()) as d
168
  )
169
  analyze_btn = gr.Button("Analyze & Redact", variant="primary")
170
  gr.Markdown("### Try These Examples")
171
- gr.Examples(examples=EXAMPLES, inputs=input_text)
172
 
173
  with gr.Column():
 
174
  stats_output = gr.Markdown(label="Statistics")
175
  redacted_output = gr.Textbox(
176
  label="Redacted Output (Safe to send to LLMs)",
@@ -179,38 +204,37 @@ with gr.Blocks(title="TSZ - Thyris Safe Zone Demo", theme=gr.themes.Soft()) as d
179
  )
180
  detection_report = gr.Markdown(label="Detection Report")
181
 
182
- analyze_btn.click(fn=analyze_text, inputs=[input_text], outputs=[redacted_output, detection_report, stats_output])
183
- input_text.submit(fn=analyze_text, inputs=[input_text], outputs=[redacted_output, detection_report, stats_output])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
  # Tab 2: API Reference
186
  with gr.TabItem("πŸ“š API Reference"):
187
  gr.Markdown("""
188
  ## TSZ API Reference
189
 
190
- TSZ is an enterprise-grade PII detection and guardrails gateway. It acts as a zero-trust middleware between your applications and external systems (LLMs, SaaS APIs, third-party services).
191
-
192
- ---
193
-
194
- ### Base Information
195
-
196
- **Base URL (Docker Compose):**
197
- ```
198
- http://localhost:8080
199
- ```
200
-
201
- **Production:**
202
- ```
203
- https://tsz.your-company.com
204
- ```
205
-
206
- **Content Type:** `application/json`
207
 
208
  ---
209
 
210
  ### Confidence & Guardrails Model
211
 
212
- TSZ uses a hybrid confidence system:
213
-
214
  | Confidence | Action |
215
  |------------|--------|
216
  | < 0.30 | ALLOW (ignored) |
@@ -221,13 +245,11 @@ TSZ uses a hybrid confidence system:
221
 
222
  ### POST /detect
223
 
224
- Primary endpoint for PII detection and redaction.
225
-
226
  **Request:**
227
  ```json
228
  {
229
  "text": "My email is user@company.com",
230
- "rid": "request-123",
231
  "guardrails": ["TOXIC_LANGUAGE"]
232
  }
233
  ```
@@ -235,12 +257,12 @@ Primary endpoint for PII detection and redaction.
235
  **Response:**
236
  ```json
237
  {
238
- "redacted_text": "My email is [EMAIL]",
239
  "detections": [
240
  {
241
  "type": "EMAIL",
242
  "value": "user@company.com",
243
- "placeholder": "[EMAIL]",
244
  "start": 12,
245
  "end": 28,
246
  "confidence_score": "0.78",
@@ -258,17 +280,22 @@ Primary endpoint for PII detection and redaction.
258
  }
259
  ```
260
 
261
- **cURL Example:**
262
- ```bash
263
- curl -X POST http://localhost:8080/detect \\
264
- -H "Content-Type: application/json" \\
265
- -d '{
266
- "text": "Contact john@example.com",
267
- "rid": "req-001",
268
- "guardrails": ["TOXIC_LANGUAGE"]
269
- }'
270
  ```
271
 
 
 
 
 
 
 
272
  ---
273
 
274
  ### POST /v1/chat/completions
@@ -277,41 +304,27 @@ OpenAI-compatible LLM Gateway with built-in guardrails.
277
 
278
  **Headers:**
279
  - `X-TSZ-RID`: Request ID for audit logs
280
- - `X-TSZ-Guardrails`: Comma-separated validators (e.g., `TOXIC_LANGUAGE,PII`)
281
  - `X-TSZ-Guardrails-Mode`: `final-only` | `stream-sync` | `stream-async`
282
- - `X-TSZ-Guardrails-OnFail`: `filter` | `halt`
283
 
284
- **Request:**
285
- ```bash
286
- curl -X POST http://localhost:8080/v1/chat/completions \\
287
- -H "Content-Type: application/json" \\
288
- -H "X-TSZ-Guardrails: TOXIC_LANGUAGE" \\
289
- -d '{
290
- "model": "gpt-4",
291
- "messages": [
292
- {"role": "user", "content": "My credit card is 4111111111111111"}
 
 
 
 
 
293
  ],
294
- "stream": false
295
- }'
296
- ```
297
-
298
- **Python SDK Example:**
299
- ```python
300
- from openai import OpenAI
301
-
302
- client = OpenAI(
303
- base_url="http://localhost:8080/v1",
304
- api_key="dummy" # TSZ uses env var
305
- )
306
-
307
- resp = client.chat.completions.create(
308
- model="gpt-4",
309
- messages=[{"role": "user", "content": "Hello"}],
310
- extra_headers={
311
- "X-TSZ-Guardrails": "TOXIC_LANGUAGE,PII",
312
- "X-TSZ-Guardrails-Mode": "stream-sync"
313
- }
314
- )
315
  ```
316
 
317
  ---
@@ -324,41 +337,10 @@ resp = client.chat.completions.create(
324
  | `GET` | `/patterns` | List patterns |
325
  | `DELETE` | `/patterns/{id}` | Delete pattern |
326
 
327
- **Create Pattern:**
328
- ```json
329
- {
330
- "Name": "PHONE_NUMBER",
331
- "Regex": "\\\\+?[0-9]{10,13}",
332
- "Category": "PII",
333
- "IsActive": true,
334
- "BlockThreshold": 0.9
335
- }
336
- ```
337
-
338
- ---
339
-
340
- ### Allowlist / Blocklist
341
-
342
- | Method | Endpoint | Description |
343
- |--------|----------|-------------|
344
- | `POST` | `/allowlist` | Add trusted value |
345
- | `GET` | `/allowlist` | List allowlist |
346
- | `DELETE` | `/allowlist/{id}` | Remove item |
347
- | `POST` | `/blacklist` | Add blocked value |
348
- | `GET` | `/blacklist` | List blocklist |
349
- | `DELETE` | `/blacklist/{id}` | Remove item |
350
-
351
  ---
352
 
353
  ### Validators & Guardrails
354
 
355
- | Method | Endpoint | Description |
356
- |--------|----------|-------------|
357
- | `POST` | `/validators` | Create validator |
358
- | `GET` | `/validators` | List validators |
359
- | `DELETE` | `/validators/{id}` | Delete validator |
360
-
361
- **AI Validator Example:**
362
  ```json
363
  {
364
  "name": "TOXIC_LANGUAGE",
@@ -375,33 +357,11 @@ resp = client.chat.completions.create(
375
  | Endpoint | Description |
376
  |----------|-------------|
377
  | `GET /healthz` | Liveness probe |
378
- | `GET /ready` | Readiness probe (DB + Redis) |
379
  | `POST /admin/reload` | Clear caches |
380
 
381
  ---
382
 
383
- ### Client SDKs
384
-
385
- **Python:**
386
- ```bash
387
- pip install "tszclient-py @ git+https://github.com/thyrisAI/safe-zone.git@main"
388
- ```
389
-
390
- ```python
391
- from tszclient_py import TSZClient
392
- client = TSZClient("http://localhost:8080")
393
- result = client.detect("test@example.com")
394
- ```
395
-
396
- **Go:**
397
- ```go
398
- import "github.com/thyrisAI/safe-zone/pkg/tszclient-go"
399
- client := tszclient.New("http://localhost:8080")
400
- result, _ := client.Detect("test@example.com")
401
- ```
402
-
403
- ---
404
-
405
  πŸ“– **Full Documentation:** [API_REFERENCE.md](https://github.com/thyrisAI/safe-zone/blob/main/docs/API_REFERENCE.md)
406
  """)
407
 
@@ -428,24 +388,36 @@ TSZ will be available at `http://localhost:8080`
428
  # LLM Gateway Configuration
429
  THYRIS_AI_MODEL_URL=https://api.openai.com/v1
430
  THYRIS_AI_API_KEY=sk-your-key
431
- THYRIS_AI_MODEL=gpt-4
432
 
433
  # Confidence Thresholds
434
  CONFIDENCE_ALLOW_THRESHOLD=0.30
435
  CONFIDENCE_BLOCK_THRESHOLD=0.85
436
 
437
- # Admin API Key
438
- ADMIN_API_KEY=your-secure-admin-key
 
439
  ```
440
 
441
  ---
442
 
443
- ### Test the API
444
 
445
  ```bash
446
  curl -X POST http://localhost:8080/detect \\
447
  -H "Content-Type: application/json" \\
448
- -d '{"text": "Email: test@example.com", "rid": "test-001"}'
 
 
 
 
 
 
 
 
 
 
 
 
449
  ```
450
 
451
  ---
@@ -456,28 +428,15 @@ curl -X POST http://localhost:8080/detect \\
456
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
457
  β”‚ Your App │────▢│ TSZ │────▢│ LLM API β”‚
458
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
459
- β”‚
460
- β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”
461
- β”‚ Audit Log β”‚
462
- β”‚ + Redis β”‚
463
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
464
  ```
465
 
466
  ---
467
 
468
- ### Key Features
469
-
470
- | Feature | Description |
471
- |---------|-------------|
472
- | **Hybrid Detection** | Regex + AI-powered confidence scoring |
473
- | **Real-time Redaction** | Context-preserving placeholders |
474
- | **LLM Gateway** | OpenAI-compatible proxy with guardrails |
475
- | **Streaming Support** | sync/async modes for SSE |
476
- | **Hot-reload Rules** | Update patterns via API |
477
- | **Audit Trail** | Full logging for compliance |
478
-
479
- ---
480
-
481
  πŸ“– **Documentation:** [github.com/thyrisAI/safe-zone/docs](https://github.com/thyrisAI/safe-zone/tree/main/docs)
482
  """)
483
 
@@ -488,26 +447,35 @@ curl -X POST http://localhost:8080/detect \\
488
 
489
  Developed by **Thyris.AI** as an open-source enterprise AI security solution.
490
 
491
- ### Why TSZ?
492
 
493
- | Challenge | TSZ Solution |
494
- |-----------|--------------|
495
- | PII leaking to LLMs | Real-time detection & redaction |
496
- | GDPR/KVKK compliance | Data never leaves your perimeter |
497
- | Toxic content in AI | AI-powered guardrails |
498
- | Audit requirements | Complete request logging |
499
- | Integration complexity | OpenAI-compatible gateway |
500
 
501
  ---
502
 
503
- ### Detection Types
504
 
505
- | Category | Types |
506
- |----------|-------|
507
- | **Personal Info** | Email, Phone, SSN, TC Kimlik, DOB |
508
- | **Financial** | Credit Card, IBAN |
509
- | **Technical** | API Keys, AWS Keys, Passwords, JWT |
510
- | **Custom** | User-defined patterns |
 
 
 
 
 
 
 
 
 
511
 
512
  ---
513
 
@@ -521,15 +489,6 @@ Developed by **Thyris.AI** as an open-source enterprise AI security solution.
521
 
522
  ---
523
 
524
- ### Links
525
-
526
- - πŸ”— **GitHub:** [thyrisAI/safe-zone](https://github.com/thyrisAI/safe-zone)
527
- - 🌐 **Website:** [thyris.ai](https://thyris.ai)
528
- - πŸ“„ **License:** Apache 2.0
529
- - πŸ“– **Docs:** [Documentation](https://github.com/thyrisAI/safe-zone/tree/main/docs)
530
-
531
- ---
532
-
533
  Built with ❀️ by [Thyris.AI](https://thyris.ai)
534
  """)
535
 
 
7
 
8
  import gradio as gr
9
  import re
10
+ import uuid
11
+ import hashlib
12
  from dataclasses import dataclass
13
  from typing import List, Tuple
14
 
 
16
  class Detection:
17
  type: str
18
  value: str
19
+ placeholder: str
20
  start: int
21
  end: int
22
  confidence: str
23
+ mask_id: str
24
 
25
  class TSZDetector:
26
  PATTERNS = {
27
  "EMAIL": {
28
  "pattern": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
 
29
  "description": "Email addresses"
30
  },
31
  "PHONE": {
32
  "pattern": r'\b(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}\b',
 
33
  "description": "Phone numbers (US format)"
34
  },
35
  "PHONE_TR": {
36
  "pattern": r'\b(?:\+90|0)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{3}[-.\s]?[0-9]{2}[-.\s]?[0-9]{2}\b',
 
37
  "description": "Phone numbers (Turkey format)"
38
  },
39
  "CREDIT_CARD": {
40
  "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',
 
41
  "description": "Credit card numbers"
42
  },
43
  "SSN": {
44
  "pattern": r'\b[0-9]{3}-[0-9]{2}-[0-9]{4}\b',
 
45
  "description": "Social Security Numbers (US)"
46
  },
47
  "TC_KIMLIK": {
48
  "pattern": r'\b[1-9][0-9]{10}\b',
 
49
  "description": "Turkish National ID"
50
  },
51
  "IBAN": {
52
  "pattern": r'\b[A-Z]{2}[0-9]{2}[A-Z0-9]{4}[0-9]{7}([A-Z0-9]?){0,16}\b',
 
53
  "description": "International Bank Account Numbers"
54
  },
55
  "IP_ADDRESS": {
56
  "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',
 
57
  "description": "IPv4 addresses"
58
  },
59
  "API_KEY": {
60
  "pattern": r'\b(?:sk-[a-zA-Z0-9]{32,}|api[_-]?key[_-]?[=:]\s*["\']?[a-zA-Z0-9]{16,}["\']?)\b',
 
61
  "description": "API keys and secrets"
62
  },
63
  "AWS_KEY": {
64
  "pattern": r'\b(?:AKIA|ABIA|ACCA|ASIA)[A-Z0-9]{16}\b',
 
65
  "description": "AWS Access Keys"
66
  },
67
  "PASSWORD": {
68
  "pattern": r'(?:password|passwd|pwd)[_\s]*[=:]\s*["\']?[^\s"\']{4,}["\']?',
 
69
  "description": "Passwords in config/code"
70
  },
71
  "JWT_TOKEN": {
72
  "pattern": r'\beyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\b',
 
73
  "description": "JSON Web Tokens"
74
  }
75
  }
76
 
77
+ def _generate_mask_id(self, value: str) -> str:
78
+ """Generate a short unique mask ID based on value hash"""
79
+ hash_val = hashlib.md5(value.encode()).hexdigest()[:6]
80
+ return hash_val
81
+
82
+ def detect(self, text: str, rid: str = "NO-RID") -> Tuple[List[Detection], str]:
83
  detections = []
84
  redacted_text = text
85
+ type_counters = {}
86
 
87
  for pii_type, config in self.PATTERNS.items():
88
  pattern = config["pattern"]
 
90
  value = match.group()
91
  if pii_type == "TC_KIMLIK" and len(value) != 11:
92
  continue
93
+
94
+ # Generate unique mask ID
95
+ if pii_type not in type_counters:
96
+ type_counters[pii_type] = 0
97
+ type_counters[pii_type] += 1
98
+
99
+ mask_id = self._generate_mask_id(value)
100
+
101
+ # Format: [RID_TYPE_maskId]
102
+ placeholder = f"[{rid}_{pii_type}_{mask_id}]"
103
+
104
  detections.append(Detection(
105
  type=pii_type,
106
  value=value,
107
+ placeholder=placeholder,
108
  start=match.start(),
109
  end=match.end(),
110
+ confidence="HIGH" if len(value) > 8 else "MEDIUM",
111
+ mask_id=mask_id
112
  ))
113
 
114
+ # Sort by position (reverse) for correct replacement
115
  detections.sort(key=lambda x: x.start, reverse=True)
116
  for det in detections:
117
+ redacted_text = redacted_text[:det.start] + det.placeholder + redacted_text[det.end:]
118
+
119
+ # Re-sort for display
120
  detections.sort(key=lambda x: x.start)
121
 
122
  return detections, redacted_text
123
 
124
  detector = TSZDetector()
125
 
126
+ def analyze_text(text: str, rid: str) -> Tuple[str, str, str, str]:
127
  if not text.strip():
128
+ return "", "Please enter some text to analyze.", "", ""
129
+
130
+ # Use provided RID or generate one
131
+ if not rid.strip():
132
+ rid = f"TSZ-{uuid.uuid4().hex[:8].upper()}"
133
 
134
+ detections, redacted_text = detector.detect(text, rid)
135
 
136
  if not detections:
137
+ return text, "No sensitive information detected.", "**0** PII entities found", f"Request ID: `{rid}`"
138
 
139
  report_lines = ["### Detected Entities\n"]
140
  by_type = {}
 
148
  report_lines.append(f"\n**{pii_type}** ({desc})")
149
  for item in items:
150
  masked_value = item.value[:3] + "***" + item.value[-2:] if len(item.value) > 5 else "***"
151
+ report_lines.append(f"- `{masked_value}` β†’ `{item.placeholder}`")
152
+ report_lines.append(f" - Mask ID: `{item.mask_id}` | Confidence: {item.confidence}")
153
 
154
  report = "\n".join(report_lines)
155
  stats = f"**{len(detections)}** PII entities found across **{len(by_type)}** categories"
156
+ rid_display = f"Request ID: `{rid}`"
157
 
158
+ return redacted_text, report, stats, rid_display
159
 
160
  EXAMPLES = [
161
+ ["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"],
162
+ ["Customer Order #12345\nName: Jane Doe\nEmail: jane.doe@example.org\nPhone: (555) 987-6543\nCredit Card: 4532015112830366", "ORDER-12345"],
163
+ ["API Configuration:\napi_key = \"sk-1234567890abcdefghijklmnopqrstuvwxyz\"\nAWS_ACCESS_KEY: AKIAIOSFODNN7EXAMPLE\npassword: \"super_secret_123\"", "CONFIG-SCAN"],
164
+ ["Merhaba, ben Ahmet Yilmaz. TC Kimlik numaram 12345678901.\nTelefon: 0532 123 45 67\nIBAN: TR330006100519786457841326", "TR-REQ-001"],
165
  ]
166
 
167
  with gr.Blocks(title="TSZ - Thyris Safe Zone Demo", theme=gr.themes.Soft()) as demo:
 
180
  with gr.TabItem("πŸ” Interactive Demo"):
181
  with gr.Row():
182
  with gr.Column():
183
+ rid_input = gr.Textbox(
184
+ label="Request ID (RID)",
185
+ placeholder="e.g., REQ-001 (auto-generated if empty)",
186
+ lines=1
187
+ )
188
  input_text = gr.Textbox(
189
  label="Input Text",
190
  placeholder="Paste text containing emails, phone numbers, credit cards, API keys...",
 
192
  )
193
  analyze_btn = gr.Button("Analyze & Redact", variant="primary")
194
  gr.Markdown("### Try These Examples")
195
+ gr.Examples(examples=EXAMPLES, inputs=[input_text, rid_input])
196
 
197
  with gr.Column():
198
+ rid_display = gr.Markdown(label="Request ID")
199
  stats_output = gr.Markdown(label="Statistics")
200
  redacted_output = gr.Textbox(
201
  label="Redacted Output (Safe to send to LLMs)",
 
204
  )
205
  detection_report = gr.Markdown(label="Detection Report")
206
 
207
+ gr.Markdown("""
208
+ ---
209
+ ### Placeholder Format
210
+
211
+ TSZ uses the format `[RID_TYPE_maskId]` for placeholders:
212
+ - **RID**: Request ID for audit correlation
213
+ - **TYPE**: Detection type (EMAIL, PHONE, etc.)
214
+ - **maskId**: Unique identifier derived from the original value
215
+
216
+ Example: `[REQ-001_EMAIL_a1b2c3]`
217
+
218
+ This allows you to:
219
+ - Track which request generated the redaction
220
+ - Identify the type of sensitive data
221
+ - Correlate with audit logs for compliance
222
+ """)
223
+
224
+ analyze_btn.click(fn=analyze_text, inputs=[input_text, rid_input], outputs=[redacted_output, detection_report, stats_output, rid_display])
225
+ input_text.submit(fn=analyze_text, inputs=[input_text, rid_input], outputs=[redacted_output, detection_report, stats_output, rid_display])
226
 
227
  # Tab 2: API Reference
228
  with gr.TabItem("πŸ“š API Reference"):
229
  gr.Markdown("""
230
  ## TSZ API Reference
231
 
232
+ TSZ is an enterprise-grade PII detection and guardrails gateway. It acts as a zero-trust middleware between your applications and external systems.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
 
234
  ---
235
 
236
  ### Confidence & Guardrails Model
237
 
 
 
238
  | Confidence | Action |
239
  |------------|--------|
240
  | < 0.30 | ALLOW (ignored) |
 
245
 
246
  ### POST /detect
247
 
 
 
248
  **Request:**
249
  ```json
250
  {
251
  "text": "My email is user@company.com",
252
+ "rid": "REQ-001",
253
  "guardrails": ["TOXIC_LANGUAGE"]
254
  }
255
  ```
 
257
  **Response:**
258
  ```json
259
  {
260
+ "redacted_text": "My email is [REQ-001_EMAIL_a1b2c3]",
261
  "detections": [
262
  {
263
  "type": "EMAIL",
264
  "value": "user@company.com",
265
+ "placeholder": "[REQ-001_EMAIL_a1b2c3]",
266
  "start": 12,
267
  "end": 28,
268
  "confidence_score": "0.78",
 
280
  }
281
  ```
282
 
283
+ ---
284
+
285
+ ### Placeholder Format
286
+
287
+ TSZ generates unique placeholders in the format:
288
+
289
+ ```
290
+ [{RID}_{TYPE}_{maskId}]
 
291
  ```
292
 
293
+ - **RID**: Request ID for audit log correlation
294
+ - **TYPE**: Detection type (EMAIL, CREDIT_CARD, etc.)
295
+ - **maskId**: Hash-based unique identifier
296
+
297
+ Example: `[RID-GW-001_EMAIL_a1b2c3]`
298
+
299
  ---
300
 
301
  ### POST /v1/chat/completions
 
304
 
305
  **Headers:**
306
  - `X-TSZ-RID`: Request ID for audit logs
307
+ - `X-TSZ-Guardrails`: Comma-separated validators
308
  - `X-TSZ-Guardrails-Mode`: `final-only` | `stream-sync` | `stream-async`
 
309
 
310
+ **Response with tsz_meta:**
311
+ ```json
312
+ {
313
+ "id": "chatcmpl-58",
314
+ "choices": [...],
315
+ "tsz_meta": {
316
+ "rid": "RID-GW-001",
317
+ "guardrails": ["TOXIC_LANGUAGE"],
318
+ "input": [
319
+ {
320
+ "redacted_text": "My email is [RID-GW-001_EMAIL_a1b2c3]",
321
+ "detections": [...],
322
+ "blocked": false
323
+ }
324
  ],
325
+ "output": [...]
326
+ }
327
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  ```
329
 
330
  ---
 
337
  | `GET` | `/patterns` | List patterns |
338
  | `DELETE` | `/patterns/{id}` | Delete pattern |
339
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  ---
341
 
342
  ### Validators & Guardrails
343
 
 
 
 
 
 
 
 
344
  ```json
345
  {
346
  "name": "TOXIC_LANGUAGE",
 
357
  | Endpoint | Description |
358
  |----------|-------------|
359
  | `GET /healthz` | Liveness probe |
360
+ | `GET /ready` | Readiness probe |
361
  | `POST /admin/reload` | Clear caches |
362
 
363
  ---
364
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
  πŸ“– **Full Documentation:** [API_REFERENCE.md](https://github.com/thyrisAI/safe-zone/blob/main/docs/API_REFERENCE.md)
366
  """)
367
 
 
388
  # LLM Gateway Configuration
389
  THYRIS_AI_MODEL_URL=https://api.openai.com/v1
390
  THYRIS_AI_API_KEY=sk-your-key
 
391
 
392
  # Confidence Thresholds
393
  CONFIDENCE_ALLOW_THRESHOLD=0.30
394
  CONFIDENCE_BLOCK_THRESHOLD=0.85
395
 
396
+ # PII Mode
397
+ PII_MODE=MASK # or BLOCK
398
+ GATEWAY_BLOCK_MODE=BLOCK # BLOCK, MASK, or WARN
399
  ```
400
 
401
  ---
402
 
403
+ ### Test with Request ID
404
 
405
  ```bash
406
  curl -X POST http://localhost:8080/detect \\
407
  -H "Content-Type: application/json" \\
408
+ -d '{
409
+ "text": "Email: test@example.com",
410
+ "rid": "MY-REQ-001"
411
+ }'
412
+ ```
413
+
414
+ **Response:**
415
+ ```json
416
+ {
417
+ "redacted_text": "Email: [MY-REQ-001_EMAIL_abc123]",
418
+ "detections": [...],
419
+ "contains_pii": true
420
+ }
421
  ```
422
 
423
  ---
 
428
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
429
  β”‚ Your App │────▢│ TSZ │────▢│ LLM API β”‚
430
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
431
+ β”‚ β”‚
432
+ β”‚ β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”
433
+ β”‚ β”‚ Audit Log β”‚
434
+ β”‚ β”‚ (with RID)β”‚
435
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Άβ””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
436
  ```
437
 
438
  ---
439
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  πŸ“– **Documentation:** [github.com/thyrisAI/safe-zone/docs](https://github.com/thyrisAI/safe-zone/tree/main/docs)
441
  """)
442
 
 
447
 
448
  Developed by **Thyris.AI** as an open-source enterprise AI security solution.
449
 
450
+ ### Key Features
451
 
452
+ | Feature | Description |
453
+ |---------|-------------|
454
+ | **Hybrid Detection** | Regex + AI-powered confidence scoring |
455
+ | **Traceable Redaction** | `[RID_TYPE_maskId]` format for audit |
456
+ | **LLM Gateway** | OpenAI-compatible proxy |
457
+ | **Streaming Support** | sync/async modes for SSE |
458
+ | **Guardrails** | AI validators (toxic, schema, custom) |
459
 
460
  ---
461
 
462
+ ### Placeholder System
463
 
464
+ Each redacted value gets a unique, traceable placeholder:
465
+
466
+ ```
467
+ [RID-GW-001_EMAIL_a1b2c3]
468
+ β”‚ β”‚ β”‚
469
+ β”‚ β”‚ └── Unique mask ID (hash of value)
470
+ β”‚ └── Detection type
471
+ └── Request ID for correlation
472
+ ```
473
+
474
+ Benefits:
475
+ - Full audit trail
476
+ - SIEM integration
477
+ - Compliance reporting
478
+ - Debug & investigation
479
 
480
  ---
481
 
 
489
 
490
  ---
491
 
 
 
 
 
 
 
 
 
 
492
  Built with ❀️ by [Thyris.AI](https://thyris.ai)
493
  """)
494