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,TemplateExpressionGuardveRawHtmlGategerç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ı.
TemplateExpressionGuardderleme
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. |
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): 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 |
|---|---|---|
<a href="javascript:alert(1)">x</a> |
true |
<a href="about:blank#rbn-blocked">x</a> |
<img src=x onerror=alert(1)> |
true |
<img src=x> |
<a href="data:image/svg+xml;base64,AAA">x</a> |
true |
<a href="about:blank#rbn-blocked">x</a> |
<a href="https://rbncore.tr/safe">x</a> |
false |
değişmez |
<svg><script>alert(1)</script></svg> |
false |
değişmez |
Bulgu (yeni, ölçüldü): hasDangerousMarkup() ve sanitize() <script>
etiketini hiç ele almıyor — DANGEROUS_SCHEMES yalnız URI şemaları
(javascript:, vbscript:, livescript:, mocha:, data:text/html,
data:application/xhtml, data:image/svg+xml — :53) ve olay niteliklerini
kapsıyor. Meşru snippet'lerde <script> olmalıdır (analytics/AdSense tam
olarak <script> ile gelir), bu yüzden kaldırılamaz; ancak <script>alert(1)</script>
gibi ham <script> gövdesi de geçiyor. sanitize() yalnız on*= ve şema
taraması yaptığı için inline script içeriği korunur — bu, FrontendProvider
yorumunda (:111-123) bilinçli bir seçimdir ("meşru snippet'ler AYNEN korunur")
ama XSS yüzeyi on*= dışında kapatmaz.
Ayrıntı: acik-sorular.md §1.9.
5. Akış — UI handler'ları
RenderService::prepareContext() (Services/RenderService.php:68) her render'da
$this->handler($type)->prepare($type, $view, $data) çağırır:
RenderService::prepareContext('frontend', 'home', $data)
└─ handler('frontend') → FrontendHandler::prepare('frontend','home',$data) UI/FrontendHandler.php:20
└─ $data döner (BaseRender::prepare varsayılanı ile aynı)
- Frontend (49 satır): yalnız sözleşme; gerçek veri toplama
FrontendProvider::render()içinde yapılır (settings okuma, SEO, asset). - Auth (94 satır):
AuthIdentityüzerinden oturumdaki kimliği$data'ya
bağlar. Panel/frontendBaseRender::bootHarmony()'de kullanıcı verisini zaten
enjekte eder (Core/Base/Web/BaseRender.php:79); bu handler kimlik
nesnesi tarafını tamamlar. - Panel (131 satır):
PanelIdentity+ConfigMapile panel öneki ve panel
kimliğini çözer.
Tuzak: Bu üç sınıf BaseRender'ı genişletir, BaseComponent'i değil; bu
yüzden provider()/resolver() yardımcıları da kullanılabilir.
6. Yapılandırma
Handlers/ altında sabit tanımlayan Config sınıfı yoktur — güvenlik
listeleri sınıf içi private const'tur:
| Sabit | Kapsam | Kaynak |
|---|---|---|
TemplateExpressionGuard::DENIED_FUNCTIONS |
~60 islev: kod çalıştırma, dosya sistemi, geri çağırma, durum, ağ | TemplateExpressionGuard.php:44 |
::DENIED_KEYWORDS |
13 dil yapısı (new, echo, print, exit, die, goto, yield, function, declare, namespace, instanceof, clone, unset) |
:75-78 |
::DENIED_STATEMENTS |
4 (include, include_once, require, require_once) |
:87-89 |
::DENIED_PATTERNS |
6 karakter/seyrek kalıp | :96-103 |
RawHtmlGate::DANGEROUS_SCHEMES |
7 şema | RawHtmlGate.php:53 |
7. Örnek (gerçek koddan)
// Core/Render/Handlers/UI/PanelHandler.php:25
public function prepare(string $type, ?string $view, array $options = []): array
// Ölçülen çıktı — guard'ın meşru değişken-fonksiyon çağrısını geçirmesi
(new TemplateExpressionGuard)->isSafe('$getEmoji($url)') // true
(new TemplateExpressionGuard)->isSafe('system("ls")') // false
8. Dark/light tema — destek tanımı ve neyin basıldığı (ColorSchemePolicy)
Tanım (tek yer): sitenin Core/Config/project-routemap.php view_mapping[<proje_anahtarı>] bloğunda
'color_schemes' => ['light', 'dark'],
Site tema düğmesini ancak hem light hem dark listelenmişse destekler (büyük/küçük harf ve boşluk önemsiz). Anahtar yok, tek şema, metin ('light,dark'), bilinmeyen ad ve true = desteksiz. Panel/admin ve auth bağlamı bu kararı kullanmaz.
| Çıktı | Destekli site | Desteksiz site |
|---|---|---|
head_state içindeki kayıtlı-tema betiği (data-theme-key / rbn_theme → <html data-theme>) |
basılır | hiç basılmaz |
rbnTheme.js (window.rbnTheme: get/set/toggle/updateIcons, tıklama dinleyicisi) |
FrontendHandler varlık olarak ekler (AssetDefinition::RBN_THEME_JS) |
eklenmez |
window.rbnReady kuyruğu (tema ile ilgisiz) |
basılır | basılır |
| Tema düğmesi | projenin kendi görünümü yazar: [data-rbn-theme-toggle] (ya da .rbn-theme-toggle, #theme-toggle), ikon [data-rbn-theme-icon] |
projede düğme olmamalı |
Koşul FrontendHandler::prepare() içinde bir kez hesaplanır; head_state partial'ı yalnız $themeToggle değişkenini okur, yapılandırmaya bakmaz. rbnDom.js tema kodu taşımaz. Not: Config view_mapping değerlerini project-settings üstüne tam anahtar olarak yazar (bkz. 02-yapilandirma §4.2); color_schemes yalnız route-map'te tanımlanır.
9. İlgili belgeler
- Core/Render genel · Builders · Providers
- Core/Base/Web.md (
BaseRender::prepare/bootHarmony) ·
Core/System/Kernel.md (panic/RBN_PANIC_ACTIVE) ·
Core/Support/Exceptions.md - Açık sorular §1.9 (
RawHtmlGate<script>kapsamı)