Ders 08 / 15
Bağımlılık ve Bileşen Taraması
Bilinen açıkların bağımlılık ağacında aranması: dolaylı bağımlılıkların payı, sürüm aralığı eşleşmesinin ürettiği yanlış alarm, önem eşiğinin taranması ve tek bir düzeltmenin sürüm aralıkları yüzünden kaç pakete birden dokunduğu.
İçindekiler
İki tarama da bu takımın yazdığı kodu sınadı. Oysa ödünç servisinin çalıştırdığı satırların çoğunu bu takım yazmadı: katalog şeması bir doğrulayıcıya, bildirim kanalı bir kuyruk istemcisine, günlük yazımı bir biçimleyiciye dayanıyor ve bunların her biri kendi bağımlılıklarını getiriyor.
Bu kodda kusur aramanın yolu taramak değil, bilinen açıkları aramaktır: bir bileşenin hangi sürüm aralığında hangi kusuru taşıdığı kayıtlıdır ve tarama bu kayıtları kurulu sürümlerle eşleştirir. Yöntem ucuzdur, ama üç yerde yanılır — ders bu üç yeri sayar.
Bağımlılık Ağacı ve Kilit Dosyası
Ödünç sisteminin dört doğrudan bağımlılığı var. Kilit dosyası, kurulu sürümleri tam sayılarla sabitler; tarama bu sürümlere bakar, bildirilen aralıklara değil.
// agac.mjs — odunc sisteminin bagimlilik agaci, kilit dosyasi ve surum araligi hesabi export const KOK = { "sema-dogrulayici": "^2.1.0", "kuyruk-istemcisi": "^1.4.0", "gunluk-bicimleyici": "^3.0.0", "kimlik-imzalayici": "^1.2.0", }; // Her paketin her surumunun kendi bildirdigi araliklar. export const DEPO = { "sema-dogrulayici": { "2.1.4": { "desen-derleyici": "^1.0.0", "metin-normallestirici": "^0.9.0" }, "2.2.0": { "desen-derleyici": "^2.0.0", "metin-normallestirici": "^0.9.0" }, }, "kuyruk-istemcisi": { "1.4.2": { "cerceve-cozucu": "^4.2.0", "yeniden-deneme": "^1.1.0" }, "1.5.0": { "cerceve-cozucu": "^4.3.0", "yeniden-deneme": "^1.2.0" }, }, "gunluk-bicimleyici": { "3.0.1": { "metin-normallestirici": "^0.9.0", "zaman-damgasi": "^2.0.0" } }, "kimlik-imzalayici": { "1.2.0": { "sayisal-ozet": "^3.1.0" } }, "desen-derleyici": { "1.0.7": {}, "2.0.0": {} }, "metin-normallestirici": { "0.9.8": {} }, "cerceve-cozucu": { "4.2.1": {}, "4.3.0": {} }, "yeniden-deneme": { "1.1.0": {}, "1.2.0": {} }, "zaman-damgasi": { "2.0.4": {}, "2.1.0": {} }, "sayisal-ozet": { "3.1.2": {} }, }; // Kilit dosyasi: kurulu olan surumler. export const KILIT = { "sema-dogrulayici": "2.1.4", "kuyruk-istemcisi": "1.4.2", "gunluk-bicimleyici": "3.0.1", "kimlik-imzalayici": "1.2.0", "desen-derleyici": "1.0.7", "metin-normallestirici": "0.9.8", "cerceve-cozucu": "4.2.1", "yeniden-deneme": "1.1.0", "zaman-damgasi": "2.0.4", "sayisal-ozet": "3.1.2", }; export const parca = (v) => v.split(".").map(Number); export const kucuk = (a, b) => { const [x, y] = [parca(a), parca(b)]; for (let i = 0; i < 3; i += 1) if (x[i] !== y[i]) return x[i] < y[i]; return false; }; // "^a.b.c": ilk sifir olmayan basamak sabit kalir. "<a.b.c": kucuk olan her surum. export function uyar(surum, aralik) { if (aralik.startsWith("<")) return kucuk(surum, aralik.slice(1)); const t = parca(aralik.slice(1)), s = parca(surum); if (kucuk(surum, aralik.slice(1))) return false; const i = t.findIndex((x) => x > 0); return s.slice(0, i + 1).join(".") === t.slice(0, i + 1).join("."); } // Kilitli surumleri izleyerek her paketin derinligini bulur. export function agac(kok = KOK, kilit = KILIT) { const derinlik = {}, sira = Object.keys(kok).map((a) => [a, 1]); while (sira.length) { const [ad, d] = sira.shift(); if (derinlik[ad] !== undefined && derinlik[ad] <= d) continue; derinlik[ad] = d; for (const alt of Object.keys(DEPO[ad][kilit[ad]])) sira.push([alt, d + 1]); } return derinlik; }
Eşleşme ve Yanlış Alarm
Kayıt kümesi kendi kayıtlarımızdır: her kayıt bir paketi, etkilenen bir sürüm aralığını, bir önem puanını ve düzeltilmiş sürümü taşır. Tarama, kilitli sürümün etkilenen aralığa düşüp düşmediğine bakar — bu, bir metin karşılaştırmasından biraz karmaşık bir sürüm karşılaştırmasıdır ve başka hiçbir bilgi kullanmaz.
IO10 (varsayım): gerçek risk, kusurlu kodun bu sistemde çağrılmasıdır. Bir paket etkilenen aralıkta olabilir ama kusurlu işlevi bu sistemde hiç çağrılmıyor olabilir; elle doğrulanmış risk listesi bu ayrımı yapar, tarama yapamaz.
// tarama.mjs — kayit kumesinin kilit dosyasiyla eslesmesi, yanlis alarmlar ve esik taramasi import { KILIT, uyar, agac } from "./agac.mjs"; const derinlik = agac(); const dogrudan = Object.keys(derinlik).filter((a) => derinlik[a] === 1); const n = Object.keys(derinlik).length; console.log(`${n} paket, dogrudan ${dogrudan.length}, dolayli ${n - dogrudan.length}` + ` (%${((100 * (n - dogrudan.length)) / n).toFixed(0)}), en buyuk derinlik ${Math.max(...Object.values(derinlik))}`); // Kendi kayit kumemiz: paket, etkilenen aralik, onem, duzeltilmis surum. const KAYIT = [ { no: "K-01", paket: "desen-derleyici", etkilenen: "<2.0.0", onem: 80, duzelten: "2.0.0" }, { no: "K-02", paket: "metin-normallestirici", etkilenen: "<0.9.8", onem: 60, duzelten: "0.9.8" }, { no: "K-03", paket: "cerceve-cozucu", etkilenen: "<4.3.0", onem: 70, duzelten: "4.3.0" }, { no: "K-04", paket: "kuyruk-istemcisi", etkilenen: "<1.5.0", onem: 90, duzelten: "1.5.0" }, { no: "K-05", paket: "zaman-damgasi", etkilenen: "<2.1.0", onem: 30, duzelten: "2.1.0" }, ]; // IO10: elle dogrulanmis gercek risk, kusurlu kodun bu sistemde cagrilmasidir. const GERCEK = ["desen-derleyici", "kuyruk-istemcisi", "yeniden-deneme"]; const bulgu = KAYIT.filter((k) => uyar(KILIT[k.paket], k.etkilenen)); console.log(`${KAYIT.length} kayit, ${bulgu.length} eslesme, ${GERCEK.length} elle dogrulanmis risk\n`); console.log(`${"kayit".padEnd(6)}${"paket".padEnd(23)}${"kurulu".padEnd(8)}${"etkilenen".padEnd(11)}onem derinlik gercek`); for (const k of KAYIT) { const e = uyar(KILIT[k.paket], k.etkilenen); console.log(`${k.no.padEnd(6)}${k.paket.padEnd(23)}${KILIT[k.paket].padEnd(8)}${k.etkilenen.padEnd(11)}` + `${String(k.onem).padStart(4)}${String(derinlik[k.paket]).padStart(10)} ${e ? (GERCEK.includes(k.paket) ? "evet" : "hayir") : "-"}`); } console.log(`\n${"esik".padStart(4)}${"kalan".padStart(7)}${"yanlis alarm".padStart(14)}${"kacirma".padStart(9)}${"maliyet w=5".padStart(12)}`); for (const e of [20, 30, 50, 70, 80, 90]) { const k = bulgu.filter((b) => b.onem >= e); const ya = k.filter((b) => !GERCEK.includes(b.paket)).length; const kac = GERCEK.length - k.filter((b) => GERCEK.includes(b.paket)).length; console.log(`${String(e).padStart(4)}${String(k.length).padStart(7)}${String(ya).padStart(14)}${String(kac).padStart(9)}${String(ya + 5 * kac).padStart(12)}`); } console.log(`kayit kumesinde hic gecmeyen gercek risk: ${GERCEK.filter((g) => !KAYIT.some((k) => k.paket === g)).join(", ")}`);
10 paket, dogrudan 4, dolayli 6 (%60), en buyuk derinlik 2 5 kayit, 4 eslesme, 3 elle dogrulanmis risk kayit paket kurulu etkilenen onem derinlik gercek K-01 desen-derleyici 1.0.7 <2.0.0 80 2 evet K-02 metin-normallestirici 0.9.8 <0.9.8 60 2 - K-03 cerceve-cozucu 4.2.1 <4.3.0 70 2 hayir K-04 kuyruk-istemcisi 1.4.2 <1.5.0 90 1 evet K-05 zaman-damgasi 2.0.4 <2.1.0 30 2 hayir esik kalan yanlis alarm kacirma maliyet w=5 20 4 2 1 7 30 4 2 1 7 50 3 1 1 6 70 3 1 1 6 80 2 0 1 5 90 1 0 2 10 kayit kumesinde hic gecmeyen gercek risk: yeniden-deneme
Üç sayı okunmalı. Birincisi dolaylı bağımlılıkların payı: on paketin altısı hiçbir yerde bildirilmedi, dört bildirimin altından geldi. Kilit dosyası olmasa bu altı paketin hangi sürümde olduğu bile bilinmezdi ve tarama sürüm bilmeden çalışamaz.
İkincisi yanlış alarm: dört eşleşmenin ikisi gerçek risk değil. İkisi de sürüm karşılaştırması açısından doğru — kurulu sürüm etkilenen aralıkta — ama kusurlu kod yolu bu sistemde çalışmıyor. Sürüm aralığı eşleşmesi paketin varlığını ölçer, kullanımını değil.
Üçüncüsü kaçırma: yeniden-deneme içindeki kusur elle doğrulanmış risk listesinde var,
kayıt kümesinde yok. Bağımlılık taramasının yapısal sınırı budur — yalnız bildirilmiş
kusurları bulur ve hiçbir eşik bildirilmemiş bir kusuru göremez.
Eşiğin kaynağı ölçümdür: eşik taraması, önem 80’de iki yanlış alarmın da düştüğünü ve kalan iki bulgunun ikisinin de gerçek çıktığını gösteriyor; maliyet en düşük değerine orada iniyor. Ağırlık yine IO8’den geliyor.
Düzeltmenin Yayılması
Eşiği geçen iki kayıt düzeltilecek. Düzeltme tek bir sürüm numarası değiştirmek gibi görünür; sürüm aralıkları bunu engeller.
// duzeltme.mjs — bir kaydin duzeltilmesinin kac pakete dokundugu import { DEPO, KOK, KILIT, uyar, kucuk, agac } from "./agac.mjs"; const surumler = (ad) => Object.keys(DEPO[ad]).sort((a, b) => (kucuk(a, b) ? -1 : 1)); const enDusukUyan = (ad, aralik) => surumler(ad).find((v) => uyar(v, aralik)); const ustler = (ad, kilit) => Object.keys(DEPO).filter((u) => DEPO[u][kilit[u]]?.[ad]); function yukselt(kilit, paket, hedef) { const yeni = { ...kilit }, dokunulan = new Set(), engel = []; const sira = [[paket, hedef]]; while (sira.length) { const [ad, s] = sira.shift(); if (yeni[ad] === s) continue; yeni[ad] = s; dokunulan.add(ad); for (const [alt, aralik] of Object.entries(DEPO[ad][s])) if (!uyar(yeni[alt], aralik)) sira.push([alt, enDusukUyan(alt, aralik)]); for (const u of ustler(ad, yeni)) { if (uyar(s, DEPO[u][yeni[u]][ad])) continue; const uygun = surumler(u).find((v) => DEPO[u][v][ad] && uyar(s, DEPO[u][v][ad])); uygun ? sira.push([u, uygun]) : engel.push(`${u} icin uygun surum yok`); } if (KOK[ad] && !uyar(s, KOK[ad])) engel.push(`kok araligi ${KOK[ad]} ${ad}@${s} kabul etmiyor`); } return { yeni, dokunulan: [...dokunulan], engel }; } let birlesik = KILIT; for (const [no, paket, hedef] of [["K-01", "desen-derleyici", "2.0.0"], ["K-04", "kuyruk-istemcisi", "1.5.0"]]) { const { yeni, dokunulan, engel } = yukselt(KILIT, paket, hedef); console.log(`${no}: ${paket} ${KILIT[paket]} -> ${hedef}; ${dokunulan.length} pakete dokunur`); for (const a of dokunulan) console.log(` ${a.padEnd(23)}${KILIT[a]} -> ${yeni[a]}`); if (engel.length) console.log(` engel: ${engel.join("; ")}`); birlesik = yukselt(birlesik, paket, hedef).yeni; } const degisen = Object.keys(KILIT).filter((a) => birlesik[a] !== KILIT[a]); const n = Object.keys(KILIT).length; console.log(`\niki duzeltme birlikte: ${degisen.length}/${n} paket (%${((100 * degisen.length) / n).toFixed(0)}) yeni surume gecti`); console.log(`agac derinligi degismedi: ${Math.max(...Object.values(agac(KOK, birlesik)))}`); console.log(`yan etki: K-03'un isaret ettigi cerceve-cozucu ${KILIT["cerceve-cozucu"]} -> ${birlesik["cerceve-cozucu"]}`);
K-01: desen-derleyici 1.0.7 -> 2.0.0; 2 pakete dokunur desen-derleyici 1.0.7 -> 2.0.0 sema-dogrulayici 2.1.4 -> 2.2.0 K-04: kuyruk-istemcisi 1.4.2 -> 1.5.0; 3 pakete dokunur kuyruk-istemcisi 1.4.2 -> 1.5.0 cerceve-cozucu 4.2.1 -> 4.3.0 yeniden-deneme 1.1.0 -> 1.2.0 iki duzeltme birlikte: 5/10 paket (%50) yeni surume gecti agac derinligi degismedi: 2 yan etki: K-03'un isaret ettigi cerceve-cozucu 4.2.1 -> 4.3.0
İlk düzeltme yukarı yayıldı. desen-derleyici düzeltilmiş sürümü büyük basamağı
artırdığı için üstündeki paketin bildirdiği aralık onu kabul etmiyor; bu yüzden ara paket
de yükseltilmek zorunda. İkinci düzeltme aşağı yayıldı: yeni sürüm kendi bağımlılıklarından
daha yenisini istiyor. İki düzeltme birlikte on paketin beşini oynattı.
Bu sayı taramanın gerçek bedelidir. Beş paketin yeni sürümü, güvenlik kaydıyla ilgisi olmayan davranış değişiklikleri de getirir; yükseltmeyi izleyen gerileme testi kaçınılmazdır ve maliyeti bulgu sayısıyla değil dokunulan paket sayısıyla ölçülür. Son satır tersini de gösteriyor: yanlış alarm sayılan bir kayıt, başka bir düzeltmenin yan etkisiyle bedava kapandı.
Kararın sahibi: önem 80’in üstündeki eşleşme yayımı durdurur ve bir yükseltme işi açar; altındakiler bir listede birikir. Kayıt kümesi her gün değişebildiği için aynı kilit dosyası bugün yeşil, yarın kırmızı olabilir — bu tarama, kod değişmeden sonucu değişen tek denetimdir.
Özet
- Bağımlılık taraması kilit dosyasındaki sürümleri kayıt kümesindeki etkilenen aralıklarla eşleştirir; bu ağaçta on paketin altısı (%60) hiçbir yerde bildirilmemiş dolaylı bağımlılıktır.
- Dört eşleşmenin ikisi yanlış alarm çıktı: sürüm doğru eşleşti ama kusurlu kod yolu bu sistemde çağrılmıyor. Sürüm aralığı eşleşmesi varlığı ölçer, kullanımı değil.
- Yakalanan sınıf bildirilmiş bir kusurun kurulu sürümde bulunmasıdır; kaçan sınıf henüz bildirilmemiş kusurdur — elle doğrulanmış üç riskten biri hiçbir kayıtta geçmiyordu.
- Eşik taraması önem 80’i verdi: 0 yanlış kalma, 1 yanlış geçme. Eşiğin kaynağı bu ölçüm, ağırlığın kaynağı IO8’dir.
- Bedel dokunulan paket sayısıdır: iki düzeltme sürüm aralıkları üzerinden yukarı ve aşağı yayılarak on paketin beşini yeni sürüme taşıdı ve her biri gerileme testi ister.
- Bu tarama, kod hiç değişmeden sonucu değişebilen tek denetimdir; kayıt kümesi büyüdükçe aynı kilit dosyası yeniden kırmızıya döner.
Sonraki Adım
Üç yöntem de bir kural kümesine bakıyordu: statik tarama desenlere, dinamik test yanıt imzalarına, bağımlılık taraması kayıtlara. Üçünün ortak varsayımı, aranan şeyin önceden adlandırılmış olmasıdır. Ödünç sisteminin en pahalı kusuru ise hiçbir listede yoktur: üyelik bitmiş bir okuyucunun ceza kaydını başka bir üyenin üstüne taşıyabilmesi, ayrı ayrı bakıldığında her adımı yetkili olan bir istek dizisidir. Hiçbir desen bunu adlandırmaz, çünkü kusur kodda değil kuralların birleşimindedir. Sonraki ders bu sınırı çizer: bir tehdit modeli üzerinde otomasyonun kapsayabildiği kusur sınıflarını insan muhakemesi gerektiren sınıflardan ayırır ve ikisinin arasında kalan payı sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.