İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat