# Core/Render/Handlers — veri hazırlayıcılar ve üç güvenlik kapısı
> **Doğrulanan kod tabanı:** `c23b431f` · **Tarih:** 2026-10-05 · **Yayın:** 0.9.7 = bu commit + sonrası; belge yalnız doğrulama anındaki kodu anlatır
> **Kaynak klasör:** `Core/Render/Handlers/` — **7 `*.php`** = 4 kök + `UI/` altında 3.
> **Envanter:** 7 dosyanın **7'si** anlatıldı.
> **Doğrulama platformu:** Windows + PHP 8.3, `TemplateExpressionGuard` ve `RawHtmlGate` gerçekten çalıştırılarak.
## 1. Ne işe yarar, kim kullanır
İki farklı iş bir arada:
* **`UI/` altındaki 3 sınıf (Handler adı yanıltıcı):** bunlar veri hazırlamaz,
`BaseRender::prepare()` sözleşmesini **boş doldurur** — `RenderService::prepareContext()`
her render'da `$this->handler($type)->prepare(...)` çağırır, UI handler'ları
`$data`'ya kendiliğinden ekleme yapar ve veriyi olduğu gibi döndürür.
* **Kök 3 sınıf:** güvenlik kapıları. `TemplateExpressionGuard` **derleme
zamanında** ifade denetler, `RawHtmlGate` **çalışma zamanında** ham HTML'i
temizler, `SchemaPreset` şema ön ayarını uygular.
## 2. Klasör/dosya envanteri (7/7)
| Dosya | Görev | Önemli public yöntemler |
|---|---|---|
| `TemplateExpressionGuard.php` (219) | Şablon ifadesi güvenlik kapısı (R-03). Yasaklı yapı listesiyle (izin listesi **değil**) fail-closed denetim yapar. | `assertSafe(string $ifade,string $baglam='sablon'): void` (hata fırlatır), `isSafe(string $ifade): bool`, `static onbellegiTemizle(): void` · korumalı: `denetle()`, `uriTehlikeliMi` benzeri yok · sabitler: `DENIED_FUNCTIONS`, `DENIED_KEYWORDS`, `DENIED_STATEMENTS`, `DENIED_PATTERNS` (hepsi `private`) |
| `RawHtmlGate.php` (149) | Veritabanından gelen ham HTML/JS snippet'lerini temizler: inline olay handler'ları ve tehlikeli URI şemalarını nötrleştirir. | `sanitize(string $html): string`, `hasDangerousMarkup(string $html): bool` · korumalı: `uriTehlikeliMi(string $deger): bool`, `dongusuzUygula(string $html,callable $adim): string` · sabitler: `EVENT_ATTR_PATTERN`, `URI_ATTR_PATTERN`, `DANGEROUS_SCHEMES` |
| `SchemaPreset.php` | Sayfa türünü (`corporate`, `home`, `blogPost`, `newsArticle`, `tvSeries`, `movie`, `person`, `productDetail`, …) JSON-LD düğümlerine çevirir; breadcrumb, `og:type` ve `og:image` aynı çağrıdan. Bilinmeyen tür `InvalidArgumentException`. Tür tablosu: [Yapisal-Veri-Head-Izin.md](Yapisal-Veri-Head-Izin.md). | `execute(object $manager,string $type,array $data=[]): void` |
| `SchemaPolicy.php` | Basılan her JSON-LD düğümünün geçtiği TEK kapı (kayıt: `handler('schemaPolicy')`): yasak alan/tipleri temizler, Product/BreadcrumbList/tarih/geo/priceRange kurallarını uygular. | `enforce(array $nodes,string $pageType=''): array`, `repaired(): array` |
| `ColorSchemePolicy.php` | "Bu site dark/light temalı mı?" sorusunun TEK cevabı: sitenin route-map bloğundaki `color_schemes` değerini okur (§9). | `static supportsToggle(): bool`, `static valueSupportsToggle(mixed $raw): bool` |
| `UI/FrontendHandler.php` (49) | `frontend` render tipi için `prepare()` sözleşmesi; tema kararını `ColorSchemePolicy`'den bir kez alır, head betiğine ve `rbnTheme.js` varlığına verir. | `prepare(string $type,?string $view,array $options=[]): array` |
| `UI/AuthHandler.php` (94) | `auth` (giriş/kayıt) ekranları için kimlik çözümü yapan `prepare()`. | `prepare(string $type,?string $view,array $options=[]): array` · kullanır: `AuthIdentity` (RbnAuth), `FrameworkIdentity` |
| `UI/PanelHandler.php` (131) | Panel ekranları için panel kimliği/önek çözümleyen `prepare()`. | `prepare(string $type,?string $view,array $options=[]): array` · kullanır: `PanelIdentity` (RbnAdmin), `ConfigMap` |
## 3. Akış — `TemplateExpressionGuard` (derleme zamanı kapısı)
`ViewEngine::parse()` her `{{ }}` ve `{!! !!}` ifadesi için
`$guard->assertSafe($m[1], $dosyaAdi)` çağırır (`ViewEngine.php:181, :190`).
```
ViewEngine::parse()
└─ TemplateExpressionGuard::assertSafe($ifade, $dosyaAdi) Handlers/TemplateExpressionGuard.php:114
├─ boş ifade → RuntimeException :117-121
├─ statik önbellek: self::$sonucOnbellegi[$ifade] :123-129
└─ denetle($ifade, $baglam) :141-197
1. DENIED_PATTERNS — '`', '', '?>', '/*', '$$', '${' :143-149
2. DENIED_STATEMENTS — include/require (word-boundary) :152-159
3. DENIED_FUNCTIONS — 60+ islev, `islev\s*\(` deseni :164-171
4. DENIED_KEYWORDS — new/echo/print/exit/… :175-182
5. değişken-fonksiyon: yalnız $\GLOBALS…( gibi sistem çağrısı :187-196
```
### 3.1 Ölçülen sınırlar (17 ifade, `DOC-AGAC-3-dogrula3.php` V2)
| İfade | Sonuç | Neden |
|---|---|---|
| `$getEmoji($url)`, `$cleanPhone($p)` | ✅ **geçer** | Değişken-fonksiyon çağrısı meşru; guard yalnız süperglobal çağrısını yasaklar (`:191`) |
| `system_errors`, `$required_role` | ✅ geçer | `DENIED_FUNCTIONS`/`KEYWORDS` word-boundary kullanır (`system(`, `system` değil) |
| `$GLOBALS["x"]` | ✅ geçer | Çağrı **değil** dizin erişimi; `$(...)` deseni tutmuyor |
| `$a ?? "b"`, `count($items) > 2` | ✅ geçer | Yasaklı kalıp yok |
| `str_contains($a,"?x=/admin")` | ✅ geçer | Metin içinde `include`/`require` kelimesi geçse bile word-boundary + `()` şartı kurtarır |
| `` `ls` `` | ❌ red | `DENIED_PATTERNS['`']` |
| `system("ls")`, `eval("1")` | ❌ red | `DENIED_FUNCTIONS` |
| `file_put_contents("/x","y")`, `file_exists("/x")`, `array_map("x",$y)` | ❌ red | `DENIED_FUNCTIONS` (dosya sistemi + geri çağırma tabanlı) |
| `new X()` | ❌ red | `DENIED_KEYWORDS['new']` |
| `foo /* yorum`, `${x}`, `$$x` | ❌ red | `DENIED_PATTERNS` |
**Tuzak (tasarım, kod yorumu `:20-26`):** Bu **izin listesi değil**, yasaklı-yapı
listesidir. Korpusta meşru değişken-fonksiyon çağrıları var; yasaklama onları
kırardı. Sonuç: **guard, "bu ifade güvenli mi" değil "bu ifade bu yasaklı kalıplardan
birini içeriyor mu" sorusuna cevap verir.** `DENIED_FUNCTIONS` listesinde
`file_exists`/`is_dir` **varken**, listedeki `file_get_contents` de var — yani
bir şablon diskteki dosya varlığını bile yoklayamaz.
**Önbellek sınırı yok:** `self::$sonucOnbellegi` (`:106`) yalnız
`onbellegiTemizle()` ile temizlenir (`:215`). Uzun süreli süreçlerde (cron kervanı)
bellek sınırsız büyür; `TemplateExpressionGuard.php:213` yorumu bunu kabul eder.
## 4. Akış — `RawHtmlGate` (çalışma zamanı kapısı)
`FrontendProvider::safeSnippet()` DB'den gelen 4 snippet'i bu kapıdan geçirir
(`Providers/UI/FrontendProvider.php:124-136`): `google_analytics_code`,
`google_adsense_code`, `head_scripts`, `body_scripts`.
```
RawHtmlGate::sanitize($html) Handlers/RawHtmlGate.php:61
├─ EVENT_ATTR_PATTERN → on*=="..." nitelikleri kaldırılır :42, :65-98
├─ URI_ATTR_PATTERN → href/src/action/… değerleri denetlenir :50, :115-137
│ └─ DANGEROUS_SCHEMES eşleşirse 'about:blank#rbn-blocked' :53
└─ dongusuzUygula() — özyineleme yok, tek geçiş (bounded) :138
```
Ölçülen çıktı (V9):
| Girdi | `hasDangerousMarkup` | `sanitize` |
|---|---|---|
| `x` | `true` | `x` |
| `
` | `true` | `
` |
| `x` | `true` | `x` |
| `x` | `false` | değişmez |
| `` | **`false`** | **değişmez** |
**Bulgu (yeni, ölçüldü):** `hasDangerousMarkup()` ve `sanitize()` **``
gibi **ham `