Skip to content

fix(diagnostics): sanitize sertlestirme - selfTest hatti, deny normalize, cycle guard#81

Merged
selimmujdeci merged 1 commit into
mainfrom
fix/diagnostics-sanitize-hardening
Jul 13, 2026
Merged

fix(diagnostics): sanitize sertlestirme - selfTest hatti, deny normalize, cycle guard#81
selimmujdeci merged 1 commit into
mainfrom
fix/diagnostics-sanitize-hardening

Conversation

@selimmujdeci

Copy link
Copy Markdown
Owner

PR-1 (#80) merge edildikten sonra ortaya çıkan üç sanitize boşluğu kapatılır. Tanı raporunun içeriği değişmez; sanitize hattının kapsamı ve dayanıklılığı sertleştirilir.


1. selfTest bölümü sanitize'ı TAMAMEN atlıyordu

reportSelfTestSnapshot raporu ham spread ediyordu:

const selfTest = await runSelfTest();
const payload = { ...base, source: 'self_test', selfTest };   // ← base sanitize'lı, selfTest DEĞİL

Yani deny-list, VIN/MAC/koordinat/api_key maskeleri ve derinlik tavanı bu bölüme hiç uygulanmıyordu. Ve bu bölüm masum değil — prob detail alanları serbest metin taşır:

Kaynak Ne yazıyor
runProbe catch ham Error.message
probeBackend catch ham fetch hata metni (URL + sorgu içerebilir)
probeIdleRenderLoop stack karesi / dosya yolu (tick@FullMapView.tsx:814)

Çözüm: yeni _sanitizeSection()tek kapı. inspector ve selfTest artık aynı kapıdan geçer; ileride eklenecek bir bölüm sanitize'ı atlayamaz.

Teşhis değeri kaybolmuyor: stack karesi (FullMapView.tsx:814) okunur kalır; yalnız VIN/MAC/koordinat/anahtar maskelenir.

2. DENY_KEYS camelCase'e kördü — sırlar sızıyordu

Eşleşme DENY_KEYS.has(key.toLowerCase()) idi ve sette 'api_key' vardı:

'apiKey'.toLowerCase() === 'apikey'  ≠  'api_key'   → deny'e TAKILMIYOR → SIZIYOR

Çözüm: anahtar normalize edilir (küçült + alfanümerik olmayanı at) ve tam eşleşme yapılır. api_key · apiKey · API_KEY · Api-Key → hepsi apikey.

Liste genişledi: apikey · accesstoken · refreshtoken · authorization · bearer · secret · password · jwt · token · email (+ mevcut konum/VIN/MAC/telefon/SSID).

⚠️ Tam eşleşme ZORUNLU — substring/prefix olsaydı BLOCKER olurdu

Güvenlik analizi bunu kanıtladı: deny substring eşleşseydi lat ⊂ p·lat·form → platform bölümünün tamamı (Event Bus + HAL wiring + bridge + sourceHealth sayaçları, PR #68) ve detectedPlatform sessizce silinirdi. vin prefix olsaydı vinMasked — yani VIN sızıntısını önlemek için üretilen maskeli türev — silinirdi. Gevşek eşleşme, korumaya çalıştığımız raporu boşaltırdı.

False-positive taraması: 14 üretici dosyada 326 benzersiz anahtar normalize edilip yeni listeyle kesiştirildi → meşru hiçbir alan düşmüyor. (tokenCount gibi komşu alanlar testte açıkça kilitlendi.)

3. Cycle guard yoktu + public API throw edebiliyordu

Özyinelemeyi sınırlayan tek şey MAX_DEPTH'ti; dairesel graf 6 kat açılıyordu. Ayrıca Object.keys / getter / Proxy patlarsa tüm rapor düşüyordu (_deepSanitize try/catch'siz, _triggerSnapshot 'error' döndürüyordu → 15 sağlıklı bölüm çöpe).

Çözüm — ancestor-path guard:

Naif "görülen nesneler" WeakSet'i yanlış olurdu: aynı nesne iki kardeş dalda geçerse (DAG — cycle değil) ikinci dalı sessizce boşaltırdı. Bu, PR-1'de düzelttiğimiz DTC [null,null] ile aynı sınıf sessiz veri kaybı. Bu yüzden: özyinelemeye girerken ekle, finally ile çıkarken sil → bir nesne yalnız kendi atası olduğunda cycle'dır.

  • Cycle → [CYCLE] işareti. Sessiz kayıp değil, görünür — "alan yoktu" ile "alan dairesel olduğu için atıldı" ayrı şeyler.
  • Dizi elemanıysa elenmez → dizi uzunluğu korunur, PR-1'in filtre sözleşmesi bozulmaz.
  • Getter/Proxy patlarsa yalnız o düğüm [UNREADABLE] olur, kardeş alanlar akar (_safeSection'ın düğüm-bazlı karşılığı). Tek zehirli alan artık tüm tanı raporunu öldüremez.

Kapsam dışı (bilinçli — dokunulmadı)

MAX_DEPTH=6 · MAX_ARRAY_LEN=20 · MAX_STR_LEN=2048 · mevcut MASKS (VIN/MAC/COORD/api_key=/token=) · UI · RPC · DB/migration · rıza & önizleme · teslimat modeli. Yalancı 'sent' ayrı PR.


Byte ölçümü — önce/sonra (aynı test, en zengin fixture)

Rapor Önce Sonra Fark
self_test 5157 B 5119 B −38 B
dev_inspector 5479 B 5479 B 0

self_test küçüldü (sanitize maskeleme + deny redaksiyonu); dev_inspector değişmedi çünkü inspector zaten aynı kapıdan geçiyordu. Şişme yok, ikisi de 64 KB sunucu tavanının çok altında.

Redaksiyon örnekleri (yalnız REDACTED biçiminde)

Girdi anahtarı Sonuç
apiKey / api_key / API_KEY / Api-Key REDACTED (anahtar düşer)
accessToken / access_token / ACCESS_TOKEN REDACTED
refreshToken · authorization · bearer · jwt REDACTED
secret · password · email · token REDACTED
tokenCount · platform · detectedPlatform · vinMasked · latency KORUNUR (tam eşleşme)

String değerlerde: ham VIN → [VIN] · ham MAC → [MAC] · koordinat çifti → [COORD] · api_key=…api_key=[MASKED]. Dairesel alan → [CYCLE] · okunamayan düğüm → [UNREADABLE].


Testler — 21 kilit

src/__tests__/diagnosticSanitizeHardening.test.ts (yeni, 17 test):

  • selfTest detail'indeki ham fetch hatası: api_key + koordinat maskeleniyor
  • selfTest detail'indeki stack karesi + VIN + MAC maskeleniyor, teşhis metni korunuyor
  • selfTest yapısı bozulmuyor (worst/summary/env/results; MAX_ARRAY_LEN kırpmıyor)
  • iç içe apiKey/accessToken/authorization/… redakte; tokenCount korunuyor
  • anahtar case varyasyonları (API_KEY, Api-Key, ACCESS_TOKEN) düşüyor
  • tam eşleşme: platform / detectedPlatform / vinMasked / latency korunuyor, lat düşüyor
  • circular object → throw yok, [CYCLE], kardeş alanlar akıyor
  • circular array → throw yok, eleman elenmiyor
  • paylaşılan referans (DAG) cycle sanılmıyor — naif WeakSet implementasyonu bu testi düşürür
  • aynı nesne kardeş dizide iki kez → ikisi de korunuyor
  • patlayan getter → yalnız o alan [UNREADABLE], rapor gönderiliyor
  • dairesel nesne raporun içindeyken bile snapshot gönderiliyor
  • PR-1 kazanımları: DTC codes[0] dolu · triyaj mevcut + OBD_DTC_PRESENT + ruleErrors=0 · inspector timeline signals korunuyor
  • byte ölçümü (self_test + dev_inspector) < 64 KB

regression.guards.test.ts (4 kaynak-metin kilidi, npm run guard kapsamı): selfTest ham atama yasağı + tek kapı · anahtar normalizasyonu (çıplak toLowerCase() eşleşmesi yasak) + deny anahtarları · ancestor-path cycle guard (finally + delete) · [UNREADABLE] fail-soft.
PR-1'in dizi kilidi kaldırılmadı — yeni doğru davranışa güncellendi (dizi dalı .map().filter() yerine döngüye geçti; davranış aynı: undefined eleman diziye eklenmez).

BEFORE koşumu kanıt: düzeltme yapılmadan aynı testler koşuldu → 8 kilit kırmızı (selfTest maskeleri, camelCase sızıntısı, cycle/[UNREADABLE] işaretleri).

CI

tsc -b temiz · lint 0 error · build OK · tam suite 3711 yeşil (206 dosya).


🔴 Saha doğrulama — Ledger #63

Cihazda doğrulanmadı. Kabul ölçütü: gerçek cihazda "Tanı Robotu" (self-test) raporu gönderilmeli ve gövdesinde → selfTest.results[].detail mevcut (bölüm boşalmadı) · results.length = summary toplamı · ham Error/fetch metninde VIN/MAC/koordinat/anahtar izi yok · platform ve vidMirror.vehicle.vinMasked bölümleri hâlâ var (deny meşru alan düşürmedi) · triage mevcut + ruleErrors=0 · DTC varken codes[0].code dolu · [CYCLE]/[UNREADABLE] görünmüyor · gövdede truncated yok, octet_length < 45 KB · low-tier cihazda (K24/Mali-400 veya T507) gönderim başarılı, ANR yok.

Gerçek cihazda self-test raporu görülmeden 🟢 yapılmayacak.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Drs9NzKMErjeNT24wLnDxX

…ize, cycle guard

PR-1 (#80) sonrasi ortaya cikan UC sanitize bosluğu kapatildi.

1) selfTest bolumu sanitize'i TAMAMEN atliyordu
   reportSelfTestSnapshot raporu ham spread ediyordu ({...base, selfTest}) ->
   deny-list, VIN/MAC/koordinat/api_key maskeleri ve derinlik tavani bu bolume
   UYGULANMIYORDU. Prob `detail` alanlari SERBEST METIN tasir: ham Error.message
   (runProbe catch), ham fetch hata metni (probeBackend), stack karesi/dosya yolu
   (probeIdleRenderLoop) -> maskesiz kanal.

   Yeni `_sanitizeSection()` TEK KAPI: inspector ve selfTest ayni kapidan gecer;
   ileride eklenecek bolum sanitize'i atlayamaz. Teshis degeri KAYBOLMAZ - stack
   karesi okunur kalir, yalniz VIN/MAC/koordinat/anahtar maskelenir.

2) DENY_KEYS camelCase'e KORDU
   Eslesme `key.toLowerCase()` idi, set'te 'api_key' vardi -> apiKey.toLowerCase()
   = 'apikey' != 'api_key' -> SIZIYORDU. Anahtar artik NORMALIZE edilir (kucult +
   alfanumerik olmayani at) ve TAM ESLESME yapilir.

   Liste: apikey, accesstoken, refreshtoken, authorization, bearer, secret,
   password, jwt, token, email (+ mevcut konum/VIN/MAC/telefon).

   TAM eslesme ZORUNLU - substring/prefix YASAK: `lat` substring olsaydi
   `platform` (W4E runtime teshis bolumunun TAMAMI) ve `detectedPlatform` duserdi;
   `vin` prefix olsaydi `vinMasked` (VIN sizintisini ONLEMEK icin uretilen maskeli
   turev) silinirdi -> koruma raporu bosaltirdi. 326 anahtar tarandi: false-positive YOK.

3) Cycle guard YOKTU
   Ozyinelemeyi sinirlayan tek sey MAX_DEPTH'ti. Eklenen guard ANCESTOR-PATH
   desenidir (girerken ekle, finally ile cikarken sil): naif "gorulen nesneler"
   seti PAYLASILAN REFERANSI (ayni nesne iki KARDES dalda = DAG) cycle sanip
   ikinci dali sessizce bosaltirdi - DTC [null,null] ile ayni sinif sessiz kayip.

   Cycle -> [CYCLE] isareti (sessiz kayip DEGIL, gorunur); dizi elemaniysa ELENMEZ.
   Public API artik THROW ETMEZ: getter/Proxy patlarsa yalniz o DUGUM [UNREADABLE]
   olur, kardes alanlar akar -> tek zehirli alan TUM raporu olduremez.

DEGISMEYEN (bilincli): MAX_DEPTH=6, MAX_ARRAY_LEN=20, MAX_STR_LEN=2048, mevcut
MASKS, UI, RPC, DB/migration, riza, teslimat modeli ('sent' yalani AYRI PR).

Byte olcumu (en zengin fixture, ayni test, once/sonra):
  self_test     5157 B -> 5119 B (-38 B; sanitize sonrasi hafif KUCULDU)
  dev_inspector 5479 B -> 5479 B (0; inspector zaten ayni kapidan geciyordu)

Testler: 17 davranis kilidi (yeni dosya) + 4 kaynak-metin kilidi (regresyon kasasi;
PR-1'in dizi kilidi KALDIRILMADI, yeni dogru davranisa GUNCELLENDI).
Tam suite 3711 yesil (206 dosya), tsc temiz, lint 0 error, build OK.

Ledger #63: DIRT KIRMIZI - gercek cihazda self-test raporu gorulmeden yesil YOK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Drs9NzKMErjeNT24wLnDxX
@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
car-launcher-pro Ready Ready Preview, Comment Jul 13, 2026 4:54am
carospro Ready Ready Preview, Comment Jul 13, 2026 4:54am

@selimmujdeci selimmujdeci merged commit 2c916ce into main Jul 13, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant