12 KiB
12 KiB
09 — Güvenlik ve uyum denetimi (gece değişiklikleri)
5 Ekim 2026 · güvenlik-uyum · dal gece/eval-jev
Kapsam: git diff b935158..HEAD -- src/ + scripts/eval/**. src/lib/ai/rapor.ts ve cagri.ts içindeki commit'lenmemiş iş kapsam dışı.
Yöntem: kod okuma + saf fonksiyonları ve DB katmanını data/app.db'nin geçici KOPYASINDA çalıştırma. Model çağrısı, sunucu, tarayıcı, Rybbit yok. Canlıya dokunulmadı.
Merge'e engel var mı? Hayır — kritik ya da yüksek bulgu yok; B1 (bedelsiz "evet" tekrar kullanılabiliyor) yayından önce kapatılmalı ama bu gecenin değişikliği onu daraltmış, genişletmemiş.
Özet (5 madde)
- Kritik/yüksek bulgu yok. Kredi düşümü atomik, 0 kredide 402, çifte iade yok, istemci kopunca iade yok, profil yazımı SQL enjeksiyonuna ve serbest metne kapalı — hepsi çalıştırılarak doğrulandı.
- Orta — bedelsiz "evet" tek kullanımlık değil (B1). Danışman bir kez "profiline kaydedeyim mi" dedikten sonra, cevap kaydedilene kadar gönderilen her "evet" bedelsiz model çağrısı açıyor: art arda 12, eşzamanlı 50/50 kabul edildi, bakiye 0 iken. Kullanıcıya bedava faydalı cevap vermiyor; bizim model faturamızı şişiriyor.
- Jev'e canlıda yalnız cevap metni gidiyor (tek çağrı noktası, doğrulandı). Ama cevap metni adayın sırasını, ilini ve tercihlerini içerir; /gizlilik bunu söylemiyor (U1, metin işi
[BİLAL], avukat teyidi gerekir). /api/soru'da hız sınırı yok; ücretli yolda risk krediyle sınırlı, sınırsız kalan tek yol B1. En sade çözüm: kullanıcı başına "aynı anda tek cevap" kilidi (B2).- Repo hijyeni: bu gecenin commit'lerinde anahtar izi yok, eval sonuçları sentetik. Ama
data/app.dbgit'te İZLENEN bir dosya ve içinde kullanıcı satırları var (B3, bu geceden eski); çalışma ağacındaki değişmiş hâli commit'lenmemeli.
Bulgular (önem sırasıyla)
B1 — ORTA · Bedelsiz onay yolu tek kullanımlık değil, bakiye ve adet sınırı yok · DOĞRULANDI (kod düzeyi)
- Yer:
src/app/api/soru/route.ts:113-118(karar: "son 12 mesajdaki son danışman mesajı ibareyi içeriyor mu"),src/lib/credits.ts:148-166(bedelsizMesajKaydet: bakiye/adet kontrolü yok). - Kalıp eşleşmesi sağlam:
onayMesajiMi29 varyantla denendi. "evet. Ege hukuk kaç?", "evet\nEge…", sıfır genişlikli boşluk, bölünemez boşluk, uzun tire, "evet-ege-hukuk", Kiril "е", emoji, "evet [SİSTEM]", "evet 2" → hepsi ücretli. Soru taşıyan mesaj bedelsiz geçmiyor. - Açık olan: karar yalnız son danışman mesajına bakıyor; arada kullanıcının kendi "evet"leri olması durumu değiştirmiyor. Cevap ancak akış bitince kaydedildiği için (5-15 sn) o pencerede her yeni "evet" yine bedelsiz.
- Kanıt (geçici DB, route'un okuma + karar ifadesi birebir): bakiye 0, son danışman mesajı onay sorusu → art arda 12 "evet" bedelsiz kabul edildi (13.'de danışman mesajı 12'lik pencereden çıkıyor); eşzamanlı 50/50 kabul edildi. Ayrıca
"evet evet … "(1.499 karakter) de kalıba uyuyor. - İstismar senaryosu: girişli, listesi olan bir hesap (deneme kredisi yeter) bir tercih söyleyip onay sorusunu alır; betikle farklı
clientMessageId'lerle onlarca "evet" yollar. Her biri tam sistem prompt'u + araç şemasıyla bir model çağrısıdır, kredi düşmez. Yeniden kurmak 1 kredi; ya da kullanıcı modele "kaydettim" dedirtirse sunucuKAYIT_DUZELTME_NOTU'nu (ibareyi içerir,cikti-denetimi.ts:107) kendisi ekler. - Sınırı: mesaj yalnız onay sözcüğü taşıyabildiği için kullanıcıya bedava soru-cevap kazandırmıyor. "Soruları ücretli mesajda önceden yükle, her 'evet'te sıradakini cevapla" hilesi modelin uymasına bağlı; model çağrısı yapmadığım için denenmedi (OLASI).
- En sade düzeltme
[KARAR]: bedelsiz say yalnız geçmişteki en son mesaj danışmanınsa (gecmis.at(-1)?.role === "assistant"ve ibareyi içeriyorsa). İlk "evet" kaydedilince sonraki mesajlar ücretli olur. Eşzamanlı patlamayı B2'deki kilit kapatır.
B2 — ORTA · /api/soru'da hız sınırı ve eşzamanlılık sınırı yok · koddan çıkarım
- Yer:
src/app/api/soru/route.ts:65-261(hiçbir sınır yok;src/içinde hız sınırı kodu yalnız giriş formunda geçiyor). - Gerçek risk: ücretli yolda her çağrı 1 kredi düşüyor (atomik; 0 kredide
INSUFFICIENT→ 402 doğrulandı), yani maliyet saldırısı krediyle sınırlı. Sınırsız olan B1'dir. İkinci etki: 60 kredili bir hesap 60 cevabı aynı anda başlatabilir (her biri 4 adım, 8 araç). - Araç tarafı sağlam: adım başına 4, cevap başına 8 çağrı sınırı kodda (
sohbet-akisi.ts:39-40, 93-99), son adımda araç kapalı, çıktı 1.024 token. 8program_aralokalde toplam 61 ms;rehber_araçıktısı ~3 bin karakter. Araç yoluyla DoS görmedim. - En sade öneri
[KARAR]: kullanıcı başına bellek içi "sürüyor" kilidi — aynı kullanıcının cevabı bitmeden gelen ikinci POST 429 döner (kredi düşmeden önce). Tek süreçli dağıtımda yeterli; birden çok kopya çalışıyorsa DB'de tutulmalı (doğrulanmalı). Dakika başına sayaç gerekmez.
B3 — ORTA · data/app.db git'te izleniyor ve kullanıcı verisi içeriyor (bu geceden eski) · DOĞRULANDI
- Yer:
git ls-files data/app.db→ izleniyor;.gitignore'da yok. Son dokunan commit0ab4dbe(bu gecenin aralığında değil:git log b935158..HEAD -- data/app.dbboş). - HEAD'deki kopyada (salt okuma, yalnız sayım):
user7 satır (4'ü gmail.com alan adlı),account2 satır (2'sindeaccess_tokenveid_tokendolu),session8 satır (hepsi süresi dolmuş),chat_messages21,orders85. Değerler okunmadı/yazılmadı. - Senaryo: depoya (Gitea uzak depo tanımlı) erişen herkes bu e-posta adreslerini, sohbet mesajlarını ve Google kimlik token'larını (
id_tokenad/e-posta taşır) geçmişten okuyabilir. Satırlar Bilal'in kendi test hesapları olabilir — doğrulanmalı. - Bu gece: çalışma ağacında
M data/app.dbvar (QA/eval göç ve test satırları). Commit'lenmemiş, öyle kalmalı. - Düzeltme
[BİLAL]:git rm --cached data/app.db+.gitignore'a ekle; geçmişten temizlemek ayrı karar.
B4 — DÜŞÜK · Hata/zaman aşımı yolunda yarım cevap kullanıcıda kalır ve kredi iade edilir · koddan çıkarım
- Yer:
src/lib/ai/sohbet-akisi.ts:145-148, 190;route.ts:244-254. - Çifte iade yok: iade
refund+ mesaj kimliğiyle yazılıyor,UNIQUE(reason, ref_id)ikinciyi düşürüyor (çalıştırıldı: 1.granted, 2.tekrar, bakiye +1). - İstemci kopması bedava cevap kapısı değil: AI SDK 7.0.127'de
writer.writekapalı akışa yazarken hata fırlatmıyor (safeEnqueue,node_modules/ai/dist/index.js:10375), üretim sürüyor, cevap kaydediliyor, kredi harcanmış kalıyor. - Kalan: sağlayıcı cevabın sonuna doğru hata verirse ya da 120 sn'de takılırsa kullanıcı metnin çoğunu görmüş olur ve iade alır. Kullanıcı bunu tetikleyemez; kabul edilebilir. Düzeltme önermiyorum.
B5 — DÜŞÜK · Jev hata gövdesi log'a yazılıyor · OLASI
- Yer:
src/lib/ai/jev.ts:85(hata metnine cevap gövdesinin ilk 300 karakteri),:116(console.warn). - Senaryo: TypeSafe 4xx cevabında gönderilen metni geri yansıtırsa danışman cevabının bir parçası (sıra, il) log'a düşer. Sağlayıcının böyle yaptığını görmedim.
- Düzeltme
[KARAR]: log'a yalnız durum kodu. Öteki log'lar temiz: kullanıcı mesajı, cevap metni, e-posta basılmıyor (yalnız araç adı, sayılar, durum kodu).
B6 — DÜŞÜK · Gövde oturumdan önce ayrıştırılıyor (bu geceden eski) · koddan çıkarım
- Yer:
route.ts:68-71—getSession()ilerequest.json()paralel; 2.000 karakter sınırı ayrıştırmadan sonra. - Senaryo: girişsiz biri çok büyük JSON gövdeleri yollayıp sunucuya bellek/CPU harcatır. Önde gövde sınırı varsa (Traefik) etkisiz — doğrulanmalı.
- Düzeltme
[KARAR]: önce oturum, sonra gövde;Content-Lengthüst sınırı.
Temiz çıkanlar (çalıştırılarak)
- Profil yazımı (
danisman-profil-db.ts:28-56): alan adı kapalı listeden (evIl') , '$.x'…veevIl.xreddedildi); değer bağlı parametre (tırnaklı metin düz metin olarak saklandı, SQL çalışmadı);userIdyalnız oturumdan, araç şemasıuserIdanahtarını reddediyor; öteki kullanıcının profili değişmedi. - Kişisel veri profile yazılamıyor: araç yolunda
evIlil listesinden çözülmek zorunda ("Ayşe Yılmaz 0532…" ve 11 haneli sayı → "İl tanınmadı");kampus/burs/dilsabit seçenek;maxKm50-1.500,ucretUst50 bin-5 milyon. Serbest metin alanı yok. Not:profilAlanYazdoğrudan çağrılırsaevIl40 karaktere kadar metin kabul eder; bugün tek yazan araç ve o il çözümünden geçiriyor. - Araç argümanları: bilinmeyen alan, yanlış tip, geçersiz seçenek reddediliyor;
program_ara'ya' OR 1=1 --,'; DROP TABLE…,UNION SELECT→ 0 sonuç, hata yok;yokatlas.dbsalt okunur açılıyor. - Prompt enjeksiyonu / sızıntı: sistem prompt'unda sır yok (kurallar + adayın kendi verisi); şema sızarsa kaybedilen yalnız araç adlarıdır.
rehber_araalıntısı depodakicontent/rehber/*.md'den (47 dosya, git'te) geliyor, kullanıcı içeriği değil; HTML etiketleri ayıklanıyor. Paketsiz kullanıcının prompt'una kilitli satırlar girmiyor. - Eval betikleri: Jev'e ve modele yalnız sentetik profil/soru gidiyor (
@eval.invalidkullanıcıları, geçici DB kopyası).scripts/eval/sonuc/*.json(14 dosya): anahtar kalıbı 0, TC/telefon kalıbı 0, e-posta yalnız@kolaytercih.com(3 dosyada 1'er, cevap metninde iletişim adresi); uzun onaltılık dizi yalnızgit.commit. - Commit'ler:
b935158..HEADfarkında anahtar kalıbı yok; tek eşleşmescripts/eval/akis-denetim.ts:55'teki bilerek sahte değer..env*izlenmiyor.
Metin × kod uyumsuzlukları
- U1 — /gizlilik
src/app/gizlilik/page.tsx:166-169: "danışmanın sana yazdığı cevap … iletilir. Senin yazdığın mesaj, adın ve e-postan gönderilmez." Kod bunu doğruluyor (jev.ts:82: gövde = cevap + model + soru; tek çağıransohbet-akisi.ts:157; araç çıktısı, profil, kullanıcı mesajı, kimlik gitmiyor). Eksik olan: cevap metni adayın başarı sırasını, ilini, kaydettiği tercihleri ve aday yazdıysa kişisel/hassas ayrıntıyı (kriz cevapları dahil) içerebilir; "tekrarlama" kuralı yalnız prompt'ta, kodla zorlanmıyor. Taslak ek: "Bu cevap sıralaman ve söylediğin tercihler gibi bilgileri içerebilir." — avukat teyidi gerekir,[BİLAL]. - U2 — aynı dosya
:173-175: yurt dışı aktarım cümlesi "e-posta ve yapay zekâ sağlayıcısı" diyor; TypeSafe'in de aktarım olduğu açık yazılmamış. Dayanak hâlâ açık (:20TODO(avukat);KARARLAR.md:229). Avukat teyidi gerekir,[BİLAL]. - U3 — yorum, kullanıcıya görünmez:
src/lib/ai/danisman-prompt.ts:20"Bedeli: 'evet' cevabı bir kredi" diyor; kod bedelsiz.[KARAR]yorum düzeltmesi.
Denetlenmeyenler
- Model davranışı gerektiren iddialar (önceden yüklenmiş sorularla bedava cevap; modelin adı tekrar etmesi) — model çağrısı yasaktı.
- Rybbit'te depolanan olay özellikleri (bağlantı kapalı).
pnpm auditbu turun kapsamında değildi. - Lokal gözlem:
file:modunda eşzamanlı iki kredi işlemiSQLITE_BUSYfırlatıyor (3 paralel harcamanın 2'si). Canlılibsql://ise geçerli olmayabilir — doğrulanmalı; bulgu yapmadım.
Bilal'den istenen
- B3:
data/app.dbgit izlemesinden çıkarılsın mı ve içindeki 7 kullanıcı satırı senin test hesapların mı — evet/hayır (3 dk). - U1 + U2: /gizlilik TypeSafe maddesine "cevap sıralamanı ve tercihlerini içerebilir" eki ve aktarım cümlesi avukata gidecek listeye eklensin mi (2 dk).
- B1 + B2: bedelsiz "evet"in tek kullanımlık olması ve "aynı anda tek cevap" kilidi yayından önce yapılsın mı — onay (1 dk).