Ders 03 / 12
Bağımlılık Bileşen Analizi
210 bileşenlik bir envanter bilinen açık kataloğuyla eşleştiriliyor: 139 ham ad eşleşmesinin 102'si sürüm aralığı, 6'sı geri taşınmış düzeltme denetiminde eleniyor ve geriye 31 gerçek eşleşme kalıyor; 12 kayıtsız açık hiç görülmüyor ve 6'sı kataloğun hiç tanımadığı bileşende. Etkilenen 30 bileşenin 25'i geçişli, 12'si hiç çağrılmıyor. Ad eşleşmesiyle bloklamak 80 yapının 78'ini durduruyor, sürüm aralığı 70'e, çağrılma süzgeci 57'ye indiriyor ve kalan bloke yapının kaynağı düzeltilmiş üst sürümü olmayan tek bir geçişli bileşendir.
İçindekiler
Önceki iki denetim aynı varsayımla çalışıyordu: taranan şey ekibin yazdığı koddur. Oysa çalışan sürecin büyük bölümü başka yerde yazılmış bileşenlerden gelir ve bunların açıkları çoğu zaman zaten bilinir — biri bulmuş, bir kayda geçirmiş ve yayımlamıştır. Burada iş bulmak değil eşleştirmektir: elde bir bağımlılık envanteri, karşıda bir kayıt listesi vardır.
Bağımlılık bileşen analizi (software composition analysis), bir yazılımın taşıdığı bileşenleri çıkarıp bunları bilinen açık kataloğuyla karşılaştıran denetimdir. Eşleştirmenin kendi yanlış pozitifi ve kendi yanlış negatifi vardır; ikisi de bu dersin konusudur. Güvenli Kodlama kursunun son dersi aynı çizgenin yapısını ölçmüştü — kaç bileşen var, kaçı geçişli, kaçı hiç çağrılmıyor. Burada çizge girdi olarak alınır ve ölçülen şey eşleştirmenin kendisidir.
- OD13. Envanter 210 bileşendir; 24’ü bildirim dosyasında adıyla geçen doğrudan bağımlılık,
kalanı geçişli. Bileşen adları (
b000,b001, …) ve katalog kayıtları kurgudur. - OD14. Bir katalog kaydı bir bileşen adı ile bir etkilenen sürüm aralığı taşır. Ad eşleşmesi tek başına kanıt değildir; aralık denetimi ayrı bir adımdır.
- OD15. Sürüm aralığında görünen bir bileşen yine de etkilenmemiş olabilir: düzeltme kullanılan sürüme geri taşınmış olabilir. Bu, aralık denetiminin elemediği yanlış pozitiftir.
- OD16. Katalog eksiksiz değildir. Bir bileşen katalogda hiç tanınmıyor olabilir; ayrıca bir açık henüz kayda geçmemiş olabilir. Bu iki durum kayıtsız açık adıyla tek alanda toplanır ve hiçbir eşleştirme onu göremez.
- OD17. Çağrılmak yüklü olmakla aynı şey değildir; bu ayrım envanterin ayrı bir alanıdır.
- OD18. Bir bağımlılık bulgusu, düzeltilene kadar her yapıyı durdurur. Düzeltme süresi yola bağlıdır ve model yapısı cinsinden verilir; süreler veridir, ölçüm değildir.
Ham Eşleşmeden Gerçek Etkilenmeye
// eslestirme.mjs — bagimlilik envanteri bilinen acik katalogu ile eslestiriliyor. // MODELDIR; bilesen adlari (b001...) ve katalog kayitlari kurgudur. export const TOHUM = 20260323, BILESEN = 210, DOGRUDAN = 24; const uretec = (t) => { let s = t >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; const r = uretec(TOHUM); export const CIDDIYET = ["dusuk", "orta", "yuksek", "kritik"]; // Envanter: her bilesenin bir surumu var, cagrilip cagrilmadigi ayri bir ozelliktir. // katalogda = bilinen acik katalogunun o bileseni hic taniyip tanimadigi. export const ENVANTER = Array.from({ length: BILESEN }, (_, i) => { const dogrudan = i < DOGRUDAN, katalogda = r() < 0.80; return { ad: `b${String(i).padStart(3, "0")}`, dogrudan, katalogda, surum: 1 + Math.floor(r() * 9), cagriliyor: r() < (dogrudan ? 0.86 : 0.55), ustSurum: r() < 0.62, // duzeltilmis bir ust surum yayimlanmis mi kayitsizAcik: r() < (katalogda ? 0.04 : 0.16) }; }); // Katalog kaydi: bir bilesen adini ve etkilenen surum araligini tasir. Ad eslesmesi // tek basina kanit degildir; surum araligi ve geri tasinmis duzeltme ayri denetimlerdir. export const KAYIT = []; for (const b of ENVANTER) { if (!b.katalogda) continue; for (let k = 0, n = Math.floor(r() * 2.6); k < n; k++) { const alt = 1 + Math.floor(r() * 8), ust = alt + 1 + Math.floor(r() * 4); KAYIT.push({ bilesen: b, alt, ust, ciddiyet: CIDDIYET[Math.floor(r() * 4)], aralikta: b.surum >= alt && b.surum < ust, geriTasindi: r() < 0.14, yayim: Math.floor(r() * 80) }); } } // Eleme hatti: ham ad eslesmesinden gercek etkilenmeye uc adimda inilir. export function hat() { const adim = [], gecir = (ad, liste, kalir) => { const kalan = liste.filter(kalir); adim.push({ ad, giren: liste.length, elenen: liste.length - kalan.length, kalan: kalan.length }); return kalan; }; let l = gecir("ad eslesmesi", KAYIT, () => true); l = gecir("surum araligi", l, (k) => k.aralikta); l = gecir("geri tasinmis duzeltme", l, (k) => !k.geriTasindi); return { adim, gercek: l }; } const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))).join(""); if (import.meta.url === `file://${process.argv[1]}`) { const { adim, gercek } = hat(); const kayitsiz = ENVANTER.filter((b) => b.kayitsizAcik); console.log(`model (tohum ${TOHUM}): ${BILESEN} bilesen (${DOGRUDAN} dogrudan), ` + `${ENVANTER.filter((b) => !b.katalogda).length} tanesi katalogda yok; ` + `${KAYIT.length} katalog kaydi`); const y1 = gen([24, 9, 9, 9]); console.log("\n" + y1(["eleme adimi", "giren", "elenen", "kalan"])); for (const a of adim) console.log(y1([a.ad, a.giren, a.elenen, a.kalan])); const etkilenen = [...new Set(gercek.map((k) => k.bilesen))]; const yanlisPoz = adim[0].giren - adim[2].kalan; console.log(`\nuc sayi: ${gercek.length} gercek eslesme (${etkilenen.length} bilesen), ` + `${yanlisPoz} yanlis pozitif, ${kayitsiz.length} kayitsiz acik hic gorulmuyor`); console.log(`kayitsiz aciklarin ${kayitsiz.filter((b) => !b.katalogda).length} tanesi ` + `katalogun hic tanimadigi bilesende`); const y2 = gen([22, 9, 9, 10, 13, 15]); console.log("\n" + y2(["etkilenen kume", "bilesen", "kayit", "cagrilan", "cagrilmayan", "ust surum var"])); for (const [ad, sec] of [["dogrudan", (b) => b.dogrudan], ["gecisli", (b) => !b.dogrudan]]) { const l = etkilenen.filter(sec); console.log(y2([ad, l.length, gercek.filter((k) => sec(k.bilesen)).length, l.filter((b) => b.cagriliyor).length, l.filter((b) => !b.cagriliyor).length, l.filter((b) => b.ustSurum).length])); } }
model (tohum 20260323): 210 bilesen (24 dogrudan), 49 tanesi katalogda yok; 139 katalog kaydi eleme adimi giren elenen kalan ad eslesmesi 139 0 139 surum araligi 139 102 37 geri tasinmis duzeltme 37 6 31 uc sayi: 31 gercek eslesme (30 bilesen), 108 yanlis pozitif, 12 kayitsiz acik hic gorulmuyor kayitsiz aciklarin 6 tanesi katalogun hic tanimadigi bilesende etkilenen kume bilesen kayit cagrilan cagrilmayan ust surum var dogrudan 5 5 4 1 3 gecisli 25 26 14 11 19
Eleme hattı iki farklı türde yanlış pozitif ayırıyor. Sürüm aralığı denetimi tek başına 102 kaydı, ham eşleşmenin yüzde 73’ünü siliyor. Bunlar “bu bileşen bizde var ve adı bir kayıtta geçiyor” demekten ibaret kalemlerdir; kullanılan sürüm etkilenen aralığın dışındadır. Yalnız ada bakan bir eşleştirme 139 bulgu raporlar ve raporun dörtte üçü tek bir alan karşılaştırmasıyla düşer.
İkinci adım aralık denetiminin elemediği yanlış pozitifi çıkarıyor: 6 kayıt sürüm aralığında görünüyor ama kullanılan yapı düzeltmeyi geri taşımış durumda. Bu kalemler ancak bileşenin gerçekten hangi kodu taşıdığına bakılarak ayrılır; sürüm dizesi yeterli değildir. Üç adımdan sonra 139 ham eşleşmeden 31 gerçek eşleşme kalıyor, 30 bileşende.
Üçüncü sayı kataloğun kendi sınırıdır. 12 kayıtsız açık hiçbir eşleştirmede görünmüyor ve 6’sı kataloğun hiç tanımadığı bir bileşende duruyor. Bu, bir ayar hatası değildir: eşleştirme yalnız kayda geçmiş olanı bulur, ve envanterin 49 bileşeni katalogda yoktur. Bir bağımlılık taramasının kapsama listesi, kaç bileşeni kataloğun tanıdığıdır; bu sayı raporlanmadıkça “sıfır bulgu” ile “hiç bakılmadı” ayırt edilemez.
İkinci tablo etkilenen 30 bileşeni iki eksende ayırıyor. Yalnız 5’i doğrudan bağımlılıktır; 25’i geçişlidir ve düzeltme yolu farklıdır. Doğrudan bir bileşen bildirim dosyasında tek satırla yükseltilir; geçişli bir bileşen için önce üst bileşenin yeni sürümü gerekir ve bu sürüm 19 kalemde vardır, 6 kalemde yoktur. Aynı ciddiyeti taşıyan iki kalemden birinin düzeltmesi bir işlemeye, diğerininki bir başkasının yayınına bağlıdır. İkinci eksen çağrılmadır: etkilenen 30 bileşenin 12’si süreçte yüklüdür ve hiç çalışmaz.
Bu iki eksen tek bir ciddiyet alanına sığmaz. Ciddiyet, açığın kendi ağırlığını anlatır ve bileşenin bu kod tabanındaki yerinden habersizdir. Aynı ciddiyet değerini taşıyan üç kalemden biri tek satırlık bir yükseltmeyle, biri bir başkasının yayımını bekleyerek, biri de hiç çalışmadığı için sıradan düşerek kapanır. Bu üçünü ayıran bilgi kataloğun kaydında yoktur; envanterdedir.
Bloklama Politikası ve Duran Hat
// politika.mjs — ayni kayit kumesi uc bloklama politikasindan geciyor; olculen sey // bloke edilen yapi sayisi ve bunun ne kadarinin bosa gittigidir. Sureler MODELDIR. import { ENVANTER, KAYIT, hat } from "./eslestirme.mjs"; const YAPI = 80, ESLESTIRME_DK = 2, BLOKE_DK = 30; const GERCEK = new Set(hat().gercek); const AGIR = new Set(["yuksek", "kritik"]); // Bir bulgu, duzeltilene kadar HER yapiyi durdurur. Duzeltme suresi yola baglidir: // dogrudan bagimlilik tek satirla, gecisli olan ust bilesenin yeni surumunu bekler. const sure = (k) => !GERCEK.has(k) ? 2 : k.bilesen.dogrudan ? 2 : k.bilesen.ustSurum ? 7 : 45; const POLITIKA = [ { ad: "ad + ciddiyet", tut: (k) => AGIR.has(k.ciddiyet) }, { ad: "+ surum araligi", tut: (k) => AGIR.has(k.ciddiyet) && k.aralikta }, { ad: "+ cagriliyor", tut: (k) => AGIR.has(k.ciddiyet) && k.aralikta && k.bilesen.cagriliyor }, ]; function olc(p) { const bloklayan = KAYIT.filter(p.tut), gunler = new Map(); for (const k of bloklayan) { for (let y = k.yayim; y < Math.min(YAPI, k.yayim + sure(k)); y++) { if (!gunler.has(y)) gunler.set(y, []); gunler.get(y).push(k); } } return { ad: p.ad, bloklayan: bloklayan.length, gercek: bloklayan.filter((k) => GERCEK.has(k)).length, yanlis: bloklayan.filter((k) => !GERCEK.has(k)).length, cagrilmayan: bloklayan.filter((k) => !k.bilesen.cagriliyor).length, uzun: bloklayan.filter((k) => sure(k) === 45).length, bloke: gunler.size, dk: YAPI * ESLESTIRME_DK + gunler.size * BLOKE_DK, yanlisBloke: [...gunler.values()].filter((l) => !l.some((k) => GERCEK.has(k))).length }; } const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))).join(""); const y = gen([18, 11, 9, 9, 13, 12, 14, 11]); console.log(`${YAPI} yapi; bir bulgu duzeltilene kadar her yapiyi durdurur. Eslestirme yapi ` + `basina ${ESLESTIRME_DK}, bloke yapi basina ${BLOKE_DK} model dakikasi`); console.log(y(["politika", "bloklayan", "gercek", "yanlis+", "cagrilmayan", "bloke yapi", "yanlis bloke", "toplam dk"])); const s = POLITIKA.map(olc); for (const o of s) { console.log(y([o.ad, o.bloklayan, o.gercek, o.yanlis, o.cagrilmayan, o.bloke, o.yanlisBloke, o.dk])); } // Cagrilmayan bilesendeki acik ayni ciddiyeti tasir ama duzeltme yolu ayridir. const cagrilmayan = [...GERCEK].filter((k) => !k.bilesen.cagriliyor); const hafif = [...GERCEK].filter((k) => k.bilesen.cagriliyor && !AGIR.has(k.ciddiyet)); console.log(`\nsurum araligi denetimi bloklayan kaydi ${s[0].bloklayan}'ten ` + `${s[1].bloklayan}'e, bloke yapiyi ${s[0].bloke}'den ${s[1].bloke}'e indiriyor ve ` + `hicbir gercek acigi disarida birakmiyor`); console.log(`cagrilma suzgeci ${s[1].cagrilmayan} kaydi daha cikariyor, bloke yapi ` + `${s[2].bloke}; cikarilanlarin hepsi cagrilmayan bilesendedir, cagrilan hicbir acik dusmez`); console.log(`geriye kalan ${s[2].bloke} bloke yapinin kaynagi son politikanin bloke ettigi ` + `${s[2].bloklayan} kaydin ${s[2].uzun} tanesidir: gecisli bir bilesende duran ve ` + `duzeltilmis ust surumu olmayan acik 45 yapi boyunca hatti durduruyor`); console.log(`kuyrukta ${cagrilmayan.length} cagrilmayan ve ${hafif.length} agir olmayan ` + `gercek acik is ogesi olarak duruyor; ${ENVANTER.filter((b) => b.kayitsizAcik).length} ` + `kayitsiz acik uc politikanin hicbirinde gorunmuyor`);
80 yapi; bir bulgu duzeltilene kadar her yapiyi durdurur. Eslestirme yapi basina 2, bloke yapi basina 30 model dakikasi politika bloklayan gercek yanlis+ cagrilmayan bloke yapi yanlis bloke toplam dk ad + ciddiyet 65 11 54 28 78 8 2500 + surum araligi 13 11 2 7 70 0 2260 + cagriliyor 6 6 0 0 57 0 1870 surum araligi denetimi bloklayan kaydi 65'ten 13'e, bloke yapiyi 78'den 70'e indiriyor ve hicbir gercek acigi disarida birakmiyor cagrilma suzgeci 7 kaydi daha cikariyor, bloke yapi 57; cikarilanlarin hepsi cagrilmayan bilesendedir, cagrilan hicbir acik dusmez geriye kalan 57 bloke yapinin kaynagi son politikanin bloke ettigi 6 kaydin 1 tanesidir: gecisli bir bilesende duran ve duzeltilmis ust surumu olmayan acik 45 yapi boyunca hatti durduruyor kuyrukta 12 cagrilmayan ve 13 agir olmayan gercek acik is ogesi olarak duruyor; 12 kayitsiz acik uc politikanin hicbirinde gorunmuyor
İlk satır bir bağımlılık taramasının hatta doğrudan kapı olarak konduğunda ne yaptığını gösteriyor. Yalnız ada ve ciddiyete bakan politika 65 kaydı bloklayıcı sayıyor, bunların 54’ü yanlış pozitif ve 80 yapının 78’i duruyor. İki yapı dışında hat çalışmıyor. Sürüm aralığı denetimi eklendiğinde bloklayan kayıt 13’e, boşuna duran yapı sıfıra iniyor ve tek bir gerçek açık dışarıda kalmıyor: bu, bedeli olmayan bir daraltmadır, çünkü elenen kalemlerin hepsi zaten yanlıştı.
Üçüncü satır bedeli olan daraltmadır ve dikkatli okunmalıdır. Çağrılma süzgeci 7 kaydı daha çıkarıyor ve bloke yapı 57’ye iniyor. Çıkarılan yedi kalem yanlış değildir — hepsi gerçek açıktır. Farklı olan şey erişilebilirlikleridir: bileşen süreçte yüklüdür, kod yolundan hiç çağrılmaz, dolayısıyla o açığa giden bir çağrı yoktur. Bu kalemler rapordan silinmez; hattı durdurmayan bir iş öğesine dönüşür ve bileşenin bir sonraki yükseltmesinde kapanır. Ayrımın gerekçesi ciddiyet değil ulaşılabilirliktir, ve ciddiyet alanı bu bilgiyi hiç taşımaz.
Son satır asıl maliyetin nerede olduğunu yazıyor. Bütün yanlış pozitifler elendikten sonra bile 57 yapı duruyor, ve bunun kaynağı altı bloklayan kaydın bir tanesidir: geçişli bir bileşende duran, düzeltilmiş üst sürümü henüz yayımlanmamış tek bir açık 45 yapı boyunca hattı kilitliyor. Burada eşik ayarının yapabileceği bir şey yoktur. Karar, kapının bu kalemi ne kadar tutacağıdır: süresiz bloke etmek hattı durdurur, bir istisna kaydıyla geçirmek açığı üretime taşır. Bloke edilen yapı sayısı, taramanın değil düzeltme yolunun ölçüsüdür.
Hat süresi sütunu bu denetimde diğer ikisinden farklı okunur. Eşleştirmenin kendisi yapı başına iki dakikadır; toplam 2 500 dakikanın 160’ı taramaya, kalanı duran hatta gider. Statik çözümlemede bedelin büyük bölümü inceleme zamanıydı, dinamik taramada koşum süresiydi; burada bedelin yüzde 94’ü kapının tuttuğu yapılardır. Üç denetimin de “hattan aldığı süre” sütunu vardır ama sütunu dolduran kalem her birinde başkadır.
Özet
- Ad eşleşmesi kanıt değildir: 139 ham eşleşmenin 102’si sürüm aralığı denetiminde, 6’sı geri taşınmış düzeltme denetiminde eleniyor ve geriye 31 gerçek eşleşme kalıyor.
- Kataloğun kapsamı yanlış negatifin kaynağıdır: 210 bileşenin 49’unu katalog tanımıyor ve 12 kayıtsız açık hiçbir eşleştirmede görünmüyor, 6’sı kataloğun tanımadığı bileşende.
- Etkilenen 30 bileşenin 25’i geçişlidir ve düzeltmesi üst bileşenin yeni sürümüne bağlıdır; 6 kalemde böyle bir sürüm yoktur, dolayısıyla aynı ciddiyet iki ayrı düzeltme yolu demektir.
- Yalnız ada ve ciddiyete bakan bloklama 80 yapının 78’ini durdurur; sürüm aralığı denetimi bunu 70’e indirir ve hiçbir gerçek açığı dışarıda bırakmaz.
- Çağrılma süzgeci 7 gerçek kalemi kapının dışına alır ve bloke yapıyı 57’ye indirir; bu kalemler yanlış değil, erişilemez olduğu için sırasını değiştirir ve iş öğesi olarak kuyrukta kalır.
- Kalan 57 bloke yapının kaynağı tek bir kayıttır: düzeltilmiş üst sürümü olmayan geçişli bir bileşen 45 yapı boyunca hattı kilitler, ve bu bir tarama ayarı sorunu değildir.
Sonraki Adım
Bu üç denetim de kodun ne yaptığına bakıyordu: hangi çağrı tehlikeli, hangi uç yanıt veriyor, hangi bileşen etkilenmiş. Depoda bir şey daha durur ve hiçbiri onu görmez — kodun içine yazılmış değerler. Bir erişim anahtarı yanlışlıkla bir dosyaya eklendiğinde derleme geçer, sınamalar geçer, üç tarama da temiz döner. Üstelik bu kalemin ayrı bir özelliği vardır: kaldıran işleme onu depodan silmez, tarihçede durmaya devam eder. Sonraki ders kurgu bir depo geçmişinde desen eşleşmesini koşturur, yanlış pozitif oranını ölçer ve yalnız son durumu taramakla bütün tarihçeyi taramanın maliyetini karşılaştırır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.