# 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 `