---
title: 'Güvenlik Otomasyonu ve Tedarik Zinciri'
source: 'https://academia.sh/tr/kurslar/guvenlik-otomasyonu'
language: tr
updated: '2026-08-17T04:38:41+00:00'
license: 'CC BY-SA 4.0'
lessons: 12
outcomes:
    - 'Statik ve dinamik güvenlik testlerini hatta yerleştirir ve üç sayıyı birlikte raporlar'
    - 'Yapı çıktısı kökenini doğrulanabilir hale getirir ve imzanın kanıtlamadığını yazar'
    - 'Bulguları sömürülebilirlik ve maruziyetle önceliklendirir'
---

# Güvenlik Otomasyonu ve Tedarik Zinciri

Otomatik bir denetimin bulduğu, kaçırdığı ve hattan aldığı sürenin birlikte ölçülmesi: statik çözümlemenin kapsamı ve yanlış pozitif yönetimi, çalışan uygulamayı tarayan aracın görebildiği ve göremediği yüzey, bağımlılık bileşen analizinde doğrudan ve geçişli etkilenen bileşenin ayrılması, çağrılmayan bileşendeki açığın önceliği, depo geçmişinde sır tarama ve desen eşleşmesinin yanlış pozitif oranı, konteyner imajında temel imajdan gelen ile uygulamadan gelen açığın ayrılması, şablon taramasının dağıtımdan önce yakaladığı kalem, yazılım malzeme listesinin cevaplayabildiği soru, yapı çıktısı imzalamanın kanıtladığı ve kanıtlamadığı, köken bilgisinin doğrulanabilir iddiaları, derleme ortamının yalıtımı, bağımlılık politikası ve bulguların ciddiyet, sömürülebilirlik ve maruziyetle üç kez sıralanması.

## Otomatik Denetimler

Her değişiklikte koşan denetimlerin üç sayıyla ölçülmesi: statik çözümlemede eşik değiştikçe gerçek, yanlış pozitif ve görülmeyen kalemin birlikte hareketi ve bulgu yorgunluğu, çalışan uygulamayı tarayan aracın erişebildiği ve erişemediği yüzey, bağımlılık çizgesinin bilinen açık kataloğuyla eşleştirilmesi, depo geçmişinde sır tarama ve tarihçenin maliyeti, imaj katmanlarında temel imajdan gelen ile uygulamadan gelen açığın ayrılması, şablon taramasının dağıtımdan önce yakaladığı kalem.

### Statik Uygulama Güvenlik Testi

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/statik-uygulama-guvenlik-testi

720 kod noktasında statik çözümlemenin üç sayısı: eşik 0,20'den 0,80'e çıkarken bulgu 418'den 56'ya, yanlış pozitif 317'den 6'ya iner ama gerçek bulgu da 101'den 50'ye düşer; 62 açık nokta eşikten bağımsız olarak aracın görüş alanı dışındadır. Aynı kuyruk geliştiriciye verildiğinde 0,20 eşiği 101 gerçek bulgunun 80'ini hiç açılmadan kapattırır, 0,65 eşiği 72 gerçek bulgunun 65'ini inceletir; gerçek bulgu başına inceleme süresi 26,9 dakikadan 10,2 dakikaya iner.

Güvenli Kodlama kursu on yedi ölçüm boyunca aynı soruyu sordu: bir savunma kalıbı kaç noktayı
kendiliğinden kapatıyor, kaç nokta dışarıda kalıyor. Bütün o envanterler elle kurulmuştu ve kod
değiştikçe eskiyen tek bir fotoğraftı. Bu kurs aynı soruyu her değişiklikte soran düzeneği ölçer:
hatta koşan otomatik denetimler.

Bir denetimin değeri "kaç bulgu çıkardığı" değildir. Üç sayı birlikte okunur: kaç kalem tarandı ve
kaç bulgu çıktı, bulguların kaçı gerçek ve kaçı yanlış pozitif, gerçekte var olup **hiç
görülmeyen** kaç kalem kaldı. Üçüncüsü yazılmadıkça ilk ikisi bir güvence değil bir yanılsamadır.
Dördüncü bir sütun da vardır ve o hattadır: denetim kaç dakika ekliyor, kaç yapıyı durduruyor,
kaçını yanlış yere durduruyor. Bu ders kursun ölçü birimini bu dört sütunla kurar ve statik
çözümlemeyi örnek alır.

- **OD1.** Kod tabanı üç depodur: abone portalı, sayaç alım servisi ve ikisinin kullandığı ortak
  kitaplık. Depolar da kod tabanı da **kurgudur**; aşağıdaki sayılar bir modelin çıktısıdır.
- **OD2.** Bir **nokta**, bir aracın kural yazabildiği kod konumudur. Her noktanın gerçekte açık
  olup olmadığı modelin doğrusudur; araç bunu bilmez.
- **OD3. Statik uygulama güvenlik testi** (static application security testing), kaynak kodu
  çalıştırmadan çözümleyip açık adayı üreten denetimdir. Araç her gördüğü noktaya bir **güven
  puanı** verir; **eşik**, bulgu olarak raporlanacak en küçük puandır.
- **OD4.** Her nokta sınıfının bir **kapsam** değeri vardır: aracın o sınıfta hiç aday üretebilme
  olasılığı. Kapsamı sıfır olan sınıf **eşikten bağımsız** olarak görülmez; bu, aracın **görüş
  alanı dışıdır**.
- **OD5.** Üç sayı ayrı yazılır: **gerçek bulgu**, **yanlış pozitif** (bulgu olup açık olmayan) ve
  **görülmeyen** kalem (açık olup bulgu olmayan).
- **OD6.** Hat bedeli model dakikasıyla verilir: yapı başına 4 dakika tarama, yanlış yere bloke
  edilen yapı başına 25 dakika insan zamanı, bulgu başına 6 dakika inceleme. Süreler **veridir**,
  bu sayfada yapılmış bir ölçüm değildir.

#### Eşik Üç Sayıyı Birlikte Oynatır

```js
// tarama.mjs — statik cozumleme aracinin bulgu kumesi; esik degistikce gercek,
// yanlis pozitif ve gorulmeyen kalem birlikte olculur. MODELDIR, kod tabani kurgudur.
export const TOHUM = 20260311, NOKTA = 720, YAPI = 140, TARAMA_DK = 4, YANLIS_DK = 25;
export const INCELEME_DK = 6;
const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };
const r = uretec(TOHUM);

// kapsam = aracin o sinifta hic aday uretebilme olasiligi; 0 olan sinif esikten
// bagimsiz gorulmez, bu aracin gorus alani disidir. ayirt = puanin ayirt etme gucu.
export const SINIF = [
  { ad: "dizgi birlestirme", pay: 0.26, acik: 0.22, kapsam: 0.95, ayirt: 0.62 },
  { ad: "dis komut cagrisi", pay: 0.08, acik: 0.35, kapsam: 0.90, ayirt: 0.55 },
  { ad: "cozumleme sinirli", pay: 0.14, acik: 0.20, kapsam: 0.40, ayirt: 0.30 },
  { ad: "kriptografi kullanimi", pay: 0.12, acik: 0.28, kapsam: 0.85, ayirt: 0.70 },
  { ad: "yapilandirma sabiti", pay: 0.22, acik: 0.10, kapsam: 0.80, ayirt: 0.25 },
  { ad: "erisim denetimi", pay: 0.18, acik: 0.30, kapsam: 0.00, ayirt: 0.00 },
];
const sinifSec = (u) => { let t = 0;
  for (const s of SINIF) { t += s.pay; if (u < t) return s; } return SINIF.at(-1); };

// Her nokta bir kod konumudur; acik alani modelin dogrusudur, arac onu bilmez.
// Gorulen noktaya bir puan verilir: acik noktalar yukari, temiz noktalar asagi kayar.
export const NOKTALAR = Array.from({ length: NOKTA }, (_, i) => {
  const s = sinifSec(r());
  const acik = r() < s.acik, gordu = r() < s.kapsam, taban = (r() + r()) / 2;
  const puan = !gordu ? null
    : acik ? Math.min(0.99, taban + s.ayirt * 0.45) : Math.max(0.01, taban - s.ayirt * 0.20);
  return { i, sinif: s.ad, acik, puan, yapi: Math.floor(r() * YAPI) };
});

export function esikte(t) {
  const bulgu = NOKTALAR.filter((n) => n.puan !== null && n.puan >= t);
  const gercek = bulgu.filter((n) => n.acik), yanlis = bulgu.filter((n) => !n.acik);
  const kacan = NOKTALAR.filter((n) => n.acik && !bulgu.includes(n));
  const korNokta = kacan.filter((n) => n.puan === null);
  const bulguluYapi = new Set(bulgu.map((n) => n.yapi));
  const gercekliYapi = new Set(gercek.map((n) => n.yapi));
  const yanlisBloke = [...bulguluYapi].filter((y) => !gercekliYapi.has(y)).length;
  return { esik: t, bulgu, gercek: gercek.length, yanlis: yanlis.length, kacan: kacan.length,
    korNokta: korNokta.length, bloke: bulguluYapi.size, yanlisBloke,
    dk: YAPI * TARAMA_DK + yanlisBloke * YANLIS_DK };
}

export const ESIKLER = [0.20, 0.35, 0.50, 0.65, 0.80];
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 ACIK = NOKTALAR.filter((n) => n.acik).length;
  const KOR = NOKTALAR.filter((n) => n.acik && n.puan === null).length;
  console.log(`model (tohum ${TOHUM}): ${NOKTA} nokta, ${ACIK} tanesi gercekte acik, ` +
    `${YAPI} yapi; ${KOR} acik nokta aracin gorus alani disinda`);
  const y1 = gen([7, 8, 9, 9, 12, 11, 8, 14, 9]);
  console.log("\n" + y1(["esik", "bulgu", "gercek", "yanlis+", "gorulmeyen", "gorus disi",
    "bloke", "yanlis bloke", "hat dk"]));
  for (const t of ESIKLER) {
    const o = esikte(t);
    console.log(y1([t.toFixed(2), o.bulgu.length, o.gercek, o.yanlis, o.kacan, o.korNokta,
      o.bloke, o.yanlisBloke, o.dk]));
  }
  const o = esikte(0.50);
  const y2 = gen([22, 8, 7, 8, 9, 9, 12]);
  console.log("\nesik 0.50'de sinif dagilimi");
  console.log(y2(["nokta sinifi", "nokta", "acik", "bulgu", "gercek", "yanlis+", "gorulmeyen"]));
  for (const s of SINIF) {
    const l = NOKTALAR.filter((n) => n.sinif === s.ad);
    const b = o.bulgu.filter((n) => n.sinif === s.ad);
    console.log(y2([s.ad, l.length, l.filter((n) => n.acik).length, b.length,
      b.filter((n) => n.acik).length, b.filter((n) => !n.acik).length,
      l.filter((n) => n.acik).length - b.filter((n) => n.acik).length]));
  }
}
```

```
model (tohum 20260311): 720 nokta, 163 tanesi gercekte acik, 140 yapi; 62 acik nokta aracin gorus alani disinda

esik      bulgu   gercek  yanlis+  gorulmeyen gorus disi   bloke  yanlis bloke   hat dk
0.20        418      101      317          62         62     132            61     2085
0.35        332       99      233          64         62     130            59     2035
0.50        225       92      133          71         62     112            46     1710
0.65        120       72       48          91         62      80            21     1085
0.80         56       50        6         113         62      47             4      660

esik 0.50'de sinif dagilimi
nokta sinifi             nokta   acik   bulgu   gercek  yanlis+  gorulmeyen
dizgi birlestirme          199     47      94       41       53           6
dis komut cagrisi           52     16      24       15        9           1
cozumleme sinirli           99     23      18        8       10          15
kriptografi kullanimi       91     22      39       19       20           3
yapilandirma sabiti        144     18      50        9       41           9
erisim denetimi            135     37       0        0        0          37
```

İlk tablo eşiğin tek bir düğme olmadığını gösteriyor. 0,20'de araç 418 bulgu çıkarıyor ve
bunların 317'si yanlış pozitif — raporun yüzde 75,8'i. Eşik 0,80'e çekildiğinde yanlış pozitif 6'ya
iniyor, ama aynı hareket gerçek bulguyu da 101'den 50'ye düşürüyor. **İki sayı aynı yöne gider;
biri iyileşirken diğeri bozulur.** Eşiği "gürültüyü azaltmak için" yükseltmek, aynı işlemle
gerçek bulguların yarısını rapordan silmektir.

Beşinci sütun eşiğin hiç dokunamadığı yeri yazıyor. 163 açık noktanın 62'si her eşikte
görülmüyor; bu sayı 0,20'de de 0,80'de de aynıdır. Bunlar aracın **yanlış negatif**leridir ve
kaynağı bir ayar hatası değil, çözümlemenin yapısıdır. Sınıf tablosu nedeni gösteriyor: erişim
denetimi sınıfında 135 nokta ve 37 gerçek açık var, bulgu sayısı sıfır. Bir isteğin çağıranın
yetkisine uygun olup olmadığı kaynak metninden okunamaz, çünkü kural kodda değil iş tanımındadır.
Çözümlemesi sınırlı sınıf da benzerdir: 23 açığın 15'i görülmüyor, çünkü çağrı hedefi çalışma
zamanında belirlenen kod aracın izleyemediği bir kenardır. **Bir tarama raporunda "sıfır bulgu"
yazan sınıf, ya temiz olan ya da hiç bakılmayan sınıftır; ikisini ayıran tek şey kapsam
listesidir.**

Son üç sütun hattı ölçüyor. 140 yapının 132'si 0,20 eşiğinde duruyor ve 61'i **yalnız yanlış
pozitif yüzünden** duruyor: hattın neredeyse yarısı boşuna bloke. Bu 61 durdurma model
hesabında 1 525 dakika insan zamanına karşılık geliyor ve toplam hat bedelini 2 085 dakikaya
çıkarıyor. Eşik 0,65'e alındığında yanlış bloke 21'e, toplam 1 085 dakikaya iniyor. Aynı tabloda
gerçek bulgunun 101'den 72'ye indiği yazılıdır — kazanç bedava değildir.

Sınıf tablosu bir ayrım daha yazıyor. Yapılandırma sabiti sınıfı 144 noktayla ikinci büyük
kaynaktır ama yalnız 18'i gerçekten açıktır; 0,50 eşiğinde 50 bulgu üretir ve bunların 41'i
yanlış pozitiftir. Kriptografi kullanımı sınıfı 91 noktayla daha küçüktür ve 39 bulgunun 19'u
gerçektir. İki sınıfın eşiği ayrı ayarlanabilseydi rapor aynı gerçek bulguyu çok daha az
gürültüyle verirdi; tek bir küresel eşik, en gürültülü sınıfın davranışını bütün rapora taşır.

Bu yüzden bir taramanın çıktısı yalnız bulgu listesi değildir. Yanında iki kayıt daha durmalıdır:
hangi nokta sınıflarının tarandığı ve hangi eşikle raporlandığı. Birincisi olmadan sıfır bulgunun
anlamı belirsizdir, ikincisi olmadan iki koşumun sayıları karşılaştırılamaz. Aynı kod tabanında
eşiği 0,80'den 0,50'ye indiren bir koşum bulguyu 56'dan 225'e çıkarır; kodda hiçbir şey
değişmemiştir.

#### Bulgu Yorgunluğu

Buraya kadar sayılan her şey aracın çıktısıdır. Bulgu bir işe yarayacaksa bir kişinin onu açması
gerekir ve o kişinin dikkati sınırsız değildir.

```js
// yorgunluk.mjs — ayni bulgu kumesi gelistiriciye gidiyor: her yanlis pozitif
// inceleme olasiligini dusurur, gercek bulgu geri yukseltir. MODELDIR.
import { esikte, ESIKLER, INCELEME_DK, TOHUM } from "./tarama.mjs";

const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };

// Guven, gelistiricinin siradaki bulguya bakma olasiligidir. Bakilmayan bulgu
// hicbir bilgi vermez, bu yuzden dusen guven kendini onarmaz.
function kuyruk(t) {
  const r = uretec(TOHUM + 7);
  const liste = esikte(t).bulgu.slice().sort((a, b) => a.yapi - b.yapi || a.i - b.i);
  let guven = 1, incelenen = 0, incelenenGercek = 0, atlananGercek = 0;
  for (const n of liste) {
    if (r() >= guven) { if (n.acik) atlananGercek++; continue; }
    incelenen++;
    if (n.acik) { incelenenGercek++; guven = Math.min(1, guven + 0.10); }
    else { guven = Math.max(0.10, guven - 0.055); }
  }
  return { bulgu: liste.length, gercek: liste.filter((n) => n.acik).length,
    incelenen, incelenenGercek, atlananGercek, guven, dk: incelenen * INCELEME_DK };
}

const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j])
  : String(v).padEnd(B[0]))).join("");
const y = gen([7, 8, 9, 11, 12, 12, 11, 9, 11]);
console.log(`bulgu basina inceleme ${INCELEME_DK} model dakikasi; guven 1.00'den baslar`);
console.log(y(["esik", "bulgu", "gercek", "incelenen", "inc. gercek", "atlanan g.",
  "son guven", "inc. dk", "dk/gercek"]));
for (const t of ESIKLER) {
  const o = kuyruk(t);
  console.log(y([t.toFixed(2), o.bulgu, o.gercek, o.incelenen, o.incelenenGercek,
    o.atlananGercek, o.guven.toFixed(2), o.dk,
    o.incelenenGercek ? (o.dk / o.incelenenGercek).toFixed(1) : "-"]));
}
const dusuk = kuyruk(0.20), orta = kuyruk(0.65);
console.log(`\nesik 0.20 esik 0.65'ten ${dusuk.gercek - orta.gercek} fazla gercek bulgu ` +
  `cikariyor ama ${orta.incelenenGercek - dusuk.incelenenGercek} tanesi daha az inceleniyor`);
console.log(`0.20'de ${dusuk.atlananGercek} gercek bulgu hic acilmadan kapaniyor, ` +
  `0.65'te ${orta.atlananGercek}; gercek bulgu basina inceleme suresi ` +
  `${(dusuk.dk / dusuk.incelenenGercek).toFixed(1)} dakikadan ` +
  `${(orta.dk / orta.incelenenGercek).toFixed(1)} dakikaya iniyor`);
```

```
bulgu basina inceleme 6 model dakikasi; guven 1.00'den baslar
esik      bulgu   gercek  incelenen inc. gercek  atlanan g.  son guven  inc. dk  dk/gercek
0.20        418      101         94          21          80       0.10      564       26.9
0.35        332       99        119          36          63       0.18      714       19.8
0.50        225       92        198          82          10       0.93     1188       14.5
0.65        120       72        110          65           7       0.94      660       10.2
0.80         56       50         55          49           1       1.00      330        6.7

esik 0.20 esik 0.65'ten 29 fazla gercek bulgu cikariyor ama 44 tanesi daha az inceleniyor
0.20'de 80 gercek bulgu hic acilmadan kapaniyor, 0.65'te 7; gercek bulgu basina inceleme suresi 26.9 dakikadan 10.2 dakikaya iniyor
```

Tablo, ilk tablodaki sıralamayı tersine çeviriyor. Araç ölçüsüyle 0,20 en iyi eşiktir: 101 gerçek
bulgu, hepsinden fazlası. Kuyruk ölçüsüyle en kötü eşiktir: o 101 bulgunun yalnız **21'i
inceleniyor**, 80'i hiç açılmadan kapanıyor. 0,65 eşiği 29 gerçek bulgu daha az çıkarıyor ama
44 gerçek bulgu daha fazla inceletiyor. Bulgu sayısı ile kapatılan açık sayısı aynı yönde
gitmiyor.

Mekanizma güven sütununda yazılı. Güven, bir sonraki bulguya bakma olasılığıdır; her yanlış
pozitif onu 0,055 düşürür, her gerçek bulgu 0,10 yükseltir. 0,20 eşiğinde kuyruğun başındaki
yoğun yanlış pozitif dizisi güveni ilk yüz bulguda tabana indiriyor ve orada bırakıyor: son güven
0,10. Bir kez oraya inildiğinde onarım da durur, çünkü **bakılmayan bulgu hiçbir bilgi vermez** —
gerçek bulgu güveni ancak açıldığında yükseltebilir. **Bulgu yorgunluğu** (alert fatigue) bu
kilitlenmenin adıdır ve tek bir bulgu üzerinde görülmez; kuyruğun tamamı üzerinde ortaya çıkar.

Son sütun aynı olguyu maliyet tarafından yazıyor. 0,20'de gerçek bulgu başına 26,9 dakika
inceleniyor, 0,80'de 6,7 dakika. Aradaki fark yanlış pozitiflerin açılıp kapatılmasına giden
zamandır. Bu ders eşiğin doğru değerini vermez, çünkü doğru değer kod tabanına ve ekibin
kapasitesine bağlıdır. Verdiği şey karar kuralıdır: bir eşik **üç sayıyla birlikte** raporlanır ve
dördüncü olarak kuyruğun ne kadarının gerçekten açıldığı ölçülür. Bu dört sayı olmadan eşik
değiştirmek, hangi sütunun ödediği bilinmeden yapılan bir takastır.

#### Özet

- Bir denetim üç sayıyla ölçülür: kaç bulgu çıktı, kaçı gerçek ve kaçı yanlış pozitif, gerçekte
  var olup hiç görülmeyen kaç kalem kaldı. Dördüncü sütun hattadır: eklenen dakika ve bloke yapı.
- Eşik iki sayıyı aynı yöne oynatır: 0,20'den 0,80'e çıkarken yanlış pozitif 317'den 6'ya inerken
  gerçek bulgu da 101'den 50'ye düşer.
- 163 açık noktanın 62'si her eşikte görülmez; erişim denetimi sınıfının 37 açığının hiçbiri
  raporda yoktur, çünkü kural kaynak metninde değil iş tanımındadır.
- Hat bedeli eşiğe bağlıdır: 0,20'de 140 yapının 61'i yalnız yanlış pozitif yüzünden bloke olur ve
  toplam 2 085 dakika, 0,65'te 21 yapı ve 1 085 dakika.
- Bulgu sayısı ile kapatılan açık sayısı aynı yönde gitmez: 0,20 eşiği 101 gerçek bulgunun 21'ini
  inceletirken 0,65 eşiği 72'nin 65'ini inceletir, çünkü yoğun yanlış pozitif güveni tabana
  indirir ve bakılmayan bulgu onu geri yükseltemez.

#### Sonraki Adım

Bu dersin bütün ölçümü kaynak metni üzerindeydi: aracın gördüğü şey dosyalardaki kod
konumlarıydı ve görüş alanı dışında kalan 62 açık noktanın büyük bölümü, kararı çalışma zamanında
verilen noktalardı. O noktalara ulaşmanın bir yolu var: uygulamayı ayağa kaldırıp dışarıdan
istemek. Ama bu kez tarayıcının önünde başka bir sınır durur — çalışan bir uygulamanın her ucu
istekle erişilebilir değildir. Bir bölümü oturum açmadan yanıt vermez, bir bölümü yalnız belirli
bir sıra izlendiğinde ortaya çıkar. Sonraki ders çalışan uygulamayı tarayan aracın **erişebildiği
ve erişemediği yüzeyi** ayırır ve aynı üç sayıyı bu kez uç kümesi üzerinde verir.

### Dinamik Uygulama Güvenlik Testi

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/dinamik-uygulama-guvenlik-testi

96 uçluk bir yüzeyde çalışan uygulamayı tarayan aracın erişebildiği ve erişemediği alan: anonim gezinme 13 uca varıp 2 açık buluyor, oturum ve kayıtlı akış eklendiğinde 84 uca ve 10 açığa çıkıyor, 12 yönetim ucu her yapılandırmada dışarıda kalıyor. 28 açığın 18'i görülmüyor; 5'i erişilemediği için, 13'ü erişildiği halde. Aynı tarama hatta üç ayrı yere konuyor: her yapıda tam 5 316 dakika ve sıfır gecikme, gecelik tam 443 dakika ve 6 yapı gecikme, yalnız değişen uç 126 dakika ama 3 açığı hiç görmüyor.

Bir önceki ders statik çözümlemenin görüş alanını saydı ve 163 açık noktanın 62'sinin her eşikte
dışarıda kaldığını gösterdi. Bunların büyük bölümü kararı çalışma zamanında verilen noktalardı:
bir isteğin çağıranın yetkisine uyup uymadığı, bir başlığın gerçekten gönderilip gönderilmediği
kaynak metninden okunmuyordu. Bu noktalara ulaşmanın bir yolu var — uygulamayı ayağa kaldırıp
dışarıdan istemek.

**Dinamik uygulama güvenlik testi** (dynamic application security testing), çalışan bir uygulamaya
istek göndererek açık arayan denetimdir. Kaynak koda bakmaz, dolayısıyla dilden ve çerçeveden
bağımsızdır; buna karşılık **yalnız erişebildiğini görür**. Bu ders o sınırı sayar: kaç uca
varılıyor, kaç uca varılamıyor, varılan uçta ne kaçıyor ve bütün bunlar hattan kaç dakika alıyor.

- **OD7.** Yüzey, abone portalı ile sayaç alım servisinin 96 ucudur. Envanter **kurgudur** ve bir
  modeldir; bu sayfada hiçbir hedefe istek gönderilmez.
- **OD8.** Her ucun iki bağımsız özelliği vardır. **Keşif**, adresin nasıl bulunduğudur: sayfa
  bağlantısı, yalnız istemci betiğinin kurduğu adres ya da bir arayüz bildirimi. **Erişim**, uçtan
  geçerli yanıt alma koşuludur: anonim, oturum gerektiren, yalnız belirli bir akış sırasında
  ortaya çıkan, yönetim rolü isteyen.
- **OD9.** Bir yapılandırma, tarayıcıya verilen yetenek kümesidir. Üçü karşılaştırılır ve her biri
  bir öncekinin üstüne tek bir yetenek ekler.
- **OD10.** Her açığın bir **görülür** değeri vardır: araç o uca ulaştığında açığı yakalar mı.
  Değer ucun kendi özelliğidir, yapılandırmadan bağımsızdır; böylece yetenek eklemek bulunan
  açığı yalnız erişim üzerinden değiştirir.
- **OD11.** İstek başına 0,05 model dakikası harcanır. Süreler **veridir**, bu sayfada yapılmış
  bir ölçüm değildir.
- **OD12.** Bir kez incelenip yanlış pozitif olarak işaretlenen bulgu bastırma listesine girer ve
  sonraki koşumlarda yapıyı bir daha durdurmaz.

#### Varılan ve Varılamayan Yüzey

```js
// yuzey.mjs — calisan uygulamayi tarayan aracin erisebildigi ve erisemedigi yuzey.
// MODELDIR; uc envanteri kurgudur, hicbir hedefe istek gonderilmez.
export const TOHUM = 20260317, UC = 96, ISTEK_DK = 0.05;
const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };
const r = uretec(TOHUM);

// kesif: uc adresinin nasil bulundugu. erisim: uca gecerli yanit aldirmanin kosulu.
const KESIF = [["baglanti", 0.52], ["istemci", 0.27], ["bildirim", 0.21]];
const ERISIM = [["anonim", 0.30], ["oturum", 0.42], ["sira", 0.16], ["yonetim", 0.12]];
// Acik sinifi: gorme = arac o sinifi erisebildigi bir ucta yakalama olasiligi.
export const SINIF = [
  { ad: "yapilandirma basligi", pay: 0.22, gorme: 0.95, istek: 4 },
  { ad: "yansiyan girdi", pay: 0.26, gorme: 0.85, istek: 26 },
  { ad: "hata mesaji sizintisi", pay: 0.18, gorme: 0.75, istek: 12 },
  { ad: "erisim denetimi", pay: 0.20, gorme: 0.15, istek: 18 },
  { ad: "is akisi kurali", pay: 0.14, gorme: 0.05, istek: 22 },
];
const sec = (tablo, u) => { let t = 0;
  for (const [ad, p] of tablo) { t += p; if (u < t) return ad; } return tablo.at(-1)[0]; };
const sinifSec = (u) => { let t = 0;
  for (const s of SINIF) { t += s.pay; if (u < t) return s; } return SINIF.at(-1); };

export const UCLAR = Array.from({ length: UC }, (_, i) => {
  const kesif = sec(KESIF, r()), erisim = sec(ERISIM, r());
  const acik = r() < 0.34 ? sinifSec(r()) : null;
  // gorulur, ucun kendi ozelligidir: arac uca ulassa bile bu acigi yakalar mi.
  const gorulur = acik ? r() < acik.gorme : false;
  return { i, kesif, erisim, acik, gorulur,
    istek: 6 + (acik ? acik.istek : 14), yanlis: r() < 0.13 };
});

// Uc yapilandirma: her biri bir onceki yetenegin ustune bir tane ekler.
export const YAPILANDIRMA = [
  { ad: "anonim gezinme", kesif: ["baglanti"], erisim: ["anonim"] },
  { ad: "oturumlu gezinme", kesif: ["baglanti", "istemci"], erisim: ["anonim", "oturum"] },
  { ad: "oturum + kayitli akis", kesif: ["baglanti", "istemci", "bildirim"],
    erisim: ["anonim", "oturum", "sira"] },
];
export const erisilen = (y) =>
  UCLAR.filter((u) => y.kesif.includes(u.kesif) && y.erisim.includes(u.erisim));

export function kosum(y) {
  const varilan = erisilen(y);
  const istek = varilan.reduce((a, u) => a + u.istek, 0);
  const gercek = varilan.filter((u) => u.acik && u.gorulur);
  const yanlisPoz = varilan.filter((u) => u.yanlis);
  const acikUc = UCLAR.filter((u) => u.acik);
  return { ad: y.ad, varilan, istek, gercek, yanlisPoz,
    erisilemeyen: acikUc.filter((u) => !varilan.includes(u)).length,
    gorulmeyen: acikUc.length - gercek.length, dk: Math.round(istek * ISTEK_DK) };
}

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 acikUc = UCLAR.filter((u) => u.acik);
  console.log(`model (tohum ${TOHUM}): ${UC} uc, ${acikUc.length} tanesinde gercek acik; ` +
    `${UCLAR.filter((u) => u.erisim === "yonetim").length} uc yonetim rolu istiyor`);
  const y1 = gen([23, 9, 8, 8, 9, 9, 12, 14, 11]);
  console.log("\n" + y1(["yapilandirma", "erisilen", "istek", "bulgu", "gercek", "yanlis+",
    "gorulmeyen", "erisilemeyen", "tarama dk"]));
  const sonuc = YAPILANDIRMA.map(kosum);
  for (const o of sonuc) {
    console.log(y1([o.ad, o.varilan.length, o.istek, o.gercek.length + o.yanlisPoz.length,
      o.gercek.length, o.yanlisPoz.length, o.gorulmeyen, o.erisilemeyen, o.dk]));
  }
  const tam = sonuc.at(-1);
  const y2 = gen([23, 7, 11, 10, 14, 16]);
  console.log("\nen genis yapilandirmada acik sinifina gore");
  console.log(y2(["acik sinifi", "acik", "erisilen", "bulunan", "erisilemeyen", "erisilip kacan"]));
  for (const s of SINIF) {
    const l = acikUc.filter((u) => u.acik.ad === s.ad);
    const e = l.filter((u) => tam.varilan.includes(u));
    const b = tam.gercek.filter((u) => u.acik.ad === s.ad).length;
    console.log(y2([s.ad, l.length, e.length, b, l.length - e.length, e.length - b]));
  }
  console.log(`\nyetenek eklemenin karsiligi: erisilen uc ${sonuc[0].varilan.length} -> ` +
    `${sonuc[1].varilan.length} -> ${tam.varilan.length}, bulunan gercek acik ` +
    `${sonuc[0].gercek.length} -> ${sonuc[1].gercek.length} -> ${tam.gercek.length}, ` +
    `tarama ${sonuc[0].dk} -> ${sonuc[1].dk} -> ${tam.dk} dakika`);
}
```

```
model (tohum 20260317): 96 uc, 28 tanesinde gercek acik; 12 uc yonetim rolu istiyor

yapilandirma            erisilen   istek   bulgu   gercek  yanlis+  gorulmeyen  erisilemeyen  tarama dk
anonim gezinme                13     278       2        2        0          26            25         14
oturumlu gezinme              50    1066       9        5        4          23            19         53
oturum + kayitli akis         84    1772      17       10        7          18             5         89

en genis yapilandirmada acik sinifina gore
acik sinifi               acik   erisilen   bulunan  erisilemeyen  erisilip kacan
yapilandirma basligi         6          3         3             3               0
yansiyan girdi               5          5         5             0               0
hata mesaji sizintisi        3          3         2             0               1
erisim denetimi              7          7         0             0               7
is akisi kurali              7          5         0             2               5

yetenek eklemenin karsiligi: erisilen uc 13 -> 50 -> 84, bulunan gercek acik 2 -> 5 -> 10, tarama 14 -> 53 -> 89 dakika
```

İlk satır bir taramanın en yaygın kurulumudur: uygulamanın adresini ver ve gezmesini bekle.
96 ucun **13'üne** varıyor. 28 açığın 25'i o anda erişilemeyen bir uçtadır ve rapor iki gerçek
bulguyla kapanır. Bu rapor yanlış değildir; eksik olduğu **hiçbir yerinde yazmaz**. "İki bulgu"
sonucunu okuyan biri yüzeyin yüzde 86'sının hiç denenmediğini bilmez.

İkinci satır tarayıcıya geçerli bir oturum ve istemci tarafında kurulan adresleri veriyor:
erişilen uç 50'ye, gerçek bulgu 5'e çıkıyor. Üçüncü satır arayüz bildirimini ve kaydedilmiş akış
sırasını ekliyor — böylece yalnız belirli bir sırayla ulaşılabilen 19 uca da varılıyor. Erişilen
uç 84, gerçek bulgu 10. **Bulunan açık sayısını beşe katlayan şey aracın yeteneği değil, ona
verilen erişimdir.** 12 yönetim ucu hiçbir satırda taranmıyor, çünkü modelde tarayıcıya yönetim
kimliği verilmemiştir; bu, kapsam kararının doğrudan yanlış negatife dönüştüğü yerdir.

İkinci tablo görülmeyen 18 açığı iki gerekçeye ayırıyor ve ayrım karar taşıyor. **Beşi
erişilemediği için görülmüyor** — üç yapılandırma başlığı ve iki akış kuralı, yönetim ucundadır.
Bunlar kapsamı genişleterek kapanabilir. **On üçü erişildiği halde görülmüyor**; kapsam
genişletmek bunlara hiçbir şey yapmaz. Erişim denetimi sınıfının yedi açığının yedisine de
ulaşılıyor ve hiçbiri bulunmuyor: bir isteğin yetkisiz olduğunu anlamak, aynı isteği **iki farklı
kimlikle** gönderip yanıtları karşılaştırmayı gerektirir; tek kimlikli bir gezinme bunu yapamaz.
İş akışı kuralı sınıfı da benzerdir — bir adımın atlanabildiğini görmek için beklenen sıranın ne
olduğunu bilmek gerekir ve tarayıcı bunu bilmez.

Bir önceki dersle birlikte okununca tablo tamamlanıyor. Statik çözümleme yapılandırma başlığını
hiç görmüyordu; buradaki araç altısının üçünü buluyor. Buna karşılık erişim denetimi sınıfı her
iki denetimde de sıfır bulguyla kapanıyor. **İki denetimin birleşimi bile bir sınıfı boş
bırakıyor** ve o sınıfın hangisi olduğu ancak kapsam listesi yazıldığında görünür.

#### Hattaki Yerleşim

Tarama 89 model dakikası sürüyor. Bu süre hattaki yeri belirler ve yer, bulgunun geliştiriciye
kaç yapı sonra döndüğünü belirler.

```js
// yerlesim.mjs — ayni tarama hatta uc ayri yere konuyor: her yapida tam, her yapida
// yalniz degisen uc, gecelik tam. Sureler MODEL dakikasidir, olcum degildir.
import { YAPILANDIRMA, erisilen, kosum, ISTEK_DK, TOHUM } from "./yuzey.mjs";

const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };
const r = uretec(TOHUM + 11);
const YAPI = 60, GECE = 12;                  // gecelik kosum her 12 yapida bir
const ULASILAN = erisilen(YAPILANDIRMA.at(-1));
const TAM_DK = kosum(YAPILANDIRMA.at(-1)).dk;

// Her yapi bir kac uca dokunur. Bir acik bir yapida dogar; dogduran yapinin o uca
// dokunmus olmasi sart degildir, cunku ortak kitaplik degisikligi baska uca yansir.
const dokunan = Array.from({ length: YAPI }, () => new Set(
  Array.from({ length: 1 + Math.floor(r() * 3) }, () => ULASILAN[Math.floor(r() * ULASILAN.length)])));
const DOGAN = ULASILAN.filter((u) => u.acik && u.gorulur).map((u) => {
  const y = Math.floor(r() * YAPI);
  if (r() < 0.65) dokunan[y].add(u);          // degisiklik dogrudan o uca dokunuyor
  return { uc: u, dogus: y };
});

// Bir yerlesim, hangi yapida hangi uc kumesinin tarandigini soyler.
const YERLESIM = [
  { ad: "her yapida tam", kapi: true, tara: (y) => ULASILAN, kosar: () => true },
  { ad: "yalniz degisen uc", kapi: true, tara: (y) => [...dokunan[y]], kosar: () => true },
  { ad: "gecelik tam", kapi: false, tara: (y) => ULASILAN, kosar: (y) => y % GECE === GECE - 1 },
];

function olc(z) {
  const gorulen = new Set(), gecikme = [], blokeYapi = new Set(), yanlisYapi = new Set();
  let dk = 0;
  for (let y = 0; y < YAPI; y++) {
    if (!z.kosar(y)) continue;
    const kume = z.tara(y);
    dk += kume.reduce((a, u) => a + u.istek, 0) * ISTEK_DK;
    for (const u of kume) {
      if (u.yanlis && !gorulen.has(u)) { gorulen.add(u); yanlisYapi.add(y); }
      const d = DOGAN.find((x) => x.uc === u && x.dogus <= y && !gorulen.has(x));
      if (d) { gorulen.add(d); gecikme.push(y - d.dogus); blokeYapi.add(y); }
    }
  }
  const bulunan = gecikme.length;
  return { ad: z.ad, dk: Math.round(dk), bulunan, kacan: DOGAN.length - bulunan,
    ort: bulunan ? (gecikme.reduce((a, b) => a + b, 0) / bulunan).toFixed(1) : "-",
    enKotu: bulunan ? Math.max(...gecikme) : "-",
    bloke: z.kapi ? blokeYapi.size : 0,
    yanlisBloke: z.kapi ? [...yanlisYapi].filter((y) => !blokeYapi.has(y)).length : 0 };
}

const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j])
  : String(v).padEnd(B[0]))).join("");
const y = gen([20, 11, 10, 9, 13, 13, 8, 14]);
console.log(`${YAPI} yapi, tam tarama ${TAM_DK} model dakikasi, ` +
  `hattaki gorunur acik ${DOGAN.length}; gecikme yapi sayisidir`);
console.log(y(["yerlesim", "toplam dk", "bulunan", "kacan", "ort. gecikme", "en kotu gec.",
  "bloke", "yanlis bloke"]));
for (const z of YERLESIM) {
  const o = olc(z);
  console.log(y([o.ad, o.dk, o.bulunan, o.kacan, o.ort, o.enKotu, o.bloke, o.yanlisBloke]));
}
const [a, b, c] = YERLESIM.map(olc);
console.log(`\ntam tarama her yapida ${a.dk} dakika alip ${a.bulunan} acigi ayni yapida ` +
  `veriyor; gecelik kosum ${c.dk} dakikaya, yani ${(100 * (1 - c.dk / a.dk)).toFixed(1)}% ` +
  `daha aza iniyor ve ayni ${c.bulunan} acigi ortalama ${c.ort} yapi sonra veriyor`);
console.log(`degisen uc taramasi ${b.dk} dakikada ${b.bulunan} acik buluyor; ` +
  `${b.kacan} acik hic taranmayan bir ucta durdugu icin bulunmuyor`);
```

```
60 yapi, tam tarama 89 model dakikasi, hattaki gorunur acik 10; gecikme yapi sayisidir
yerlesim              toplam dk   bulunan    kacan ort. gecikme en kotu gec.   bloke  yanlis bloke
her yapida tam             5316        10        0          0.0            0      10             0
yalniz degisen uc           126         7        3          2.7           19       7             6
gecelik tam                 443        10        0          6.0           11       0             0

tam tarama her yapida 5316 dakika alip 10 acigi ayni yapida veriyor; gecelik kosum 443 dakikaya, yani 91.7% daha aza iniyor ve ayni 10 acigi ortalama 6.0 yapi sonra veriyor
degisen uc taramasi 126 dakikada 7 acik buluyor; 3 acik hic taranmayan bir ucta durdugu icin bulunmuyor
```

Üç satır aynı taramayı, aynı yüzeyi ve aynı on açığı ele alıyor; ayrıldıkları yer bedeldir. Her
yapıda tam tarama on açığın onunu da doğduğu yapıda veriyor — gecikme sıfır — ve karşılığında
5 316 model dakikası alıyor. Gecelik koşum aynı on açığı aynı eksiksizlikle buluyor, hattan
yüzde 91,7 daha az dakika alıyor ve hiçbir yapıyı durdurmuyor. Ödediği şey tek bir sütunda:
ortalama 6 yapı, en kötü durumda 11 yapı gecikme. Bir açık doğduktan sonra on bir yapı boyunca
üretime gidebilir.

Üçüncü satır ucuzdur ve eksiktir. Yalnız değişen uçları taramak 126 dakikada kalıyor ama **on
açığın üçünü hiç bulmuyor**. Nedeni ikinci kod bloğunun yorumunda yazılı: bir açık her zaman
kendi ucuna dokunan değişiklikte doğmaz. Ortak kitaplıkta yapılan bir değişiklik başka bir ucun
davranışını bozar, o uç altmış yapı boyunca bir daha değişmez ve değişen uç taraması oraya hiç
uğramaz. **Değişen dosyaya bakan bir denetim, değişmeyen yerde doğan açığı göremez.**

Son sütun beklenmedik yerdedir. En ucuz yerleşim, yanlış pozitif yüzünden **en çok yapıyı
durduran** yerleşimdir: 6 yapı. Tam tarama yedi yanlış pozitifin hepsini ilk koşumda çıkarır,
hepsi bir kerede elenir ve bastırma listesine girer; değişen uç taraması aynı yedi bulguyu
haftalara yayarak birer birer çıkarır ve her biri ayrı bir yapıyı durdurur. Toplam yanlış pozitif
sayısı aynıdır, hatta bıraktığı iz farklıdır.

#### Özet

- Çalışan uygulamayı tarayan araç yalnız erişebildiğini görür: anonim gezinme 96 ucun 13'üne
  varıp 2 açık bulur, oturum ve kayıtlı akış eklendiğinde 84 uca ve 10 açığa çıkar.
- Görülmeyen 18 açık iki gerekçeye ayrılır ve ayrım karar taşır: 5'i erişilemediği için (kapsam
  genişletilerek kapanır), 13'ü erişildiği halde (kapsam genişletmek etkilemez).
- Erişim denetimi sınıfının yedi açığının yedisine de ulaşılır ve hiçbiri bulunmaz; aynı sınıf
  statik çözümlemede de sıfır bulguyla kapanmıştı — iki denetimin birleşimi bile onu boş bırakır.
- Hattaki yerleşim bulunan açığı değil gecikmeyi ve bedeli değiştirir: her yapıda tam tarama
  5 316 dakika ve sıfır gecikme, gecelik koşum 443 dakika ve ortalama 6 yapı gecikme.
- Yalnız değişen uçları taramak 126 dakikaya iner ama on açığın üçünü hiç görmez, çünkü ortak
  bileşen değişikliğiyle doğan açık hiç dokunulmayan bir uçta durur.
- Yanlış pozitifin bedeli yerleşime bağlıdır: aynı yedi bulgu tam taramada bir kerede, değişen uç
  taramasında altı ayrı yapıyı durdurarak elenir.

#### Sonraki Adım

İki denetim de aynı varsayımla çalıştı: 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 bu bileşenlerin açıkları zaten bilinir —
biri onları bulmuş, kaydetmiş ve yayımlamıştır. O halde iş bulmak değil **eşleştirmektir**: elde
bir bağımlılık çizgesi ve bir kayıt listesi vardır, sorulan soru hangi kaydın hangi bileşeni
gerçekten etkilediğidir. Bu eşleştirmenin kendi yanlış pozitifi ve kendi yanlış negatifi vardır.
Sonraki ders bağımlılık çizgesini bilinen açık kataloğuyla eşleştirir, doğrudan ve geçişli
etkilenen bileşeni ayırır ve çağrılmayan bileşendeki açığın neden başka bir sıraya girdiğini
sayar.

### Bağımlılık Bileşen Analizi

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/bagimlilik-bilesen-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.

Ö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

```js
// 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

```js
// 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.

### Depo Geçmişinde Sır Tarama

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/depo-gecmisinde-sir-tarama

940 dizi sabitinde dört desenin birlikte çalışması: entropi eşiği 0,60'tan kapalıya çekildiğinde eşleşme 855'ten 519'a inerken yanlış pozitif oranı 68,0'dan ancak 52,8'e düşer, çünkü gürültünün kaynağı entropi değil 442 eşleşmenin 270'ini yanlış çıkaran anahtar sözcük desenidir; 310 sırrın 50'si hiçbir desene takılmaz ve 26'sı tek bir sınıftadır. Üç yerleşim aynı desen kümesini taşır: her yapıda çalışma ağacı 1 057 dakika tarayıp 18 sırra hiç bakmaz, artımlı fark 9 dakikaya iner ama geriye kalan 164 sırrı hiç görmez, bir kerelik tam tarihçe 48 dakikada geçmişin tamamını okur ve 14 sırrın yalnız orada durduğunu gösterir.

Bir erişim anahtarı bir yapılandırma satırına elle yazıldığında derleme geçer, sınamalar geçer ve
önceki üç denetim de temiz döner: değer geçerli bir dizedir, çalışan uçta hata üretmez, bir
bağımlılık kaydına karşılık gelmez. Onu görecek denetim dosyaların içindeki dizi sabitlerine
bakar.

**Sır tarama** (secret scanning), depo içeriğindeki dizi sabitlerini bir desen kümesinden geçirip
gizli olma ihtimali taşıyanları çıkaran denetimdir. Güvenli Kodlama kursunda aynı depo geçmişi bir
kez ölçülmüştü, ama ölçülen şey sırrın **maruziyetiydi**: bir değerin kaç işlem boyunca açık
kaldığı, silmenin neyi kapatmadığı. Burada ölçülen şey **denetimin kendisidir** — hangi desen kaç
bulgu üretiyor, gürültü hangisinden geliyor, hangi sır sınıfı hiçbir desene takılmıyor ve bütün
bunlar hattan kaç dakika alıyor.

- **OD19.** Depo geçmişi üç deponun ortak 1 800 işlemesi ve 940 dizi sabitidir; **kurgudur** ve
  hiçbir gerçek sır biçimi yazılmaz. Bir **dizi sabiti** bir dosyaya yazılmış değişmez metin
  değeridir; gerçekten gizli olup olmadığı modelin doğrusudur, tarayıcı yalnız biçime bakar.
- **OD20.** Desen kümesi dörttür. **Önek biçimi** ve **blok başlığı** yalnız biçimi belli sınıfları
  yakalar. **Anahtar sözcük ataması** değere değil, değerin atandığı **ada** bakar. **Entropi
  eşiği** eşiğin üstünde kalan her diziyi alır ve ayarlanabilen tek desendir.
- **OD21.** **Entropi**, dizinin karakter dağılımının düzensizliğini 0 ile 1 arasında veren model
  ölçüsüdür; rastgele değerler yukarıda, sözcükler aşağıda durur.
- **OD22.** Bir dizinin çalışma ağacından çıkarılması onu geçmişten çıkarmaz ve **döndürme
  değildir**; bu, Güvenli Kodlama kursunda ölçülmüştü, burada girdi olarak alınır.
- **OD23.** Tarayıcı 900. işlemede kurulur, ondan önce hiç koşmamıştır; yapılar on işlemede bir
  alınır, toplam 90 yapı. Taranan kalem başına 0,02, incelenen yanlış pozitif başına 4 model
  dakikası harcanır. Süreler **veridir**, ölçüm değildir.

#### Gürültüyü Hangi Desen Üretiyor

```js
// sir.mjs — depo gecmisindeki dizi sabitleri desen kumesinden geciriliyor; olculen sey
// eslesme, gercek sir, yanlis pozitif ve hicbir desenin gormedigi kalemdir. MODELDIR.
export const TOHUM = 20260329, ISLEME = 1800, DIZI = 940, KALEM_DK = 0.02;
const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };
const r = uretec(TOHUM);

// [ad, pay, sir olasiligi, entropi tabani, entropi tavani, ada atanma olasiligi, bicim]
// sir = o sinifin gercekten gizli olma olasiligi; bicim = onek/blok deseninin tanidigi kalip.
export const SINIF = [
  ["onekli erisim anahtari", 0.09, 0.93, 0.70, 0.96, 0.55, "onek"],
  ["rastgele oturum sirri", 0.11, 0.82, 0.66, 0.94, 0.72, ""],
  ["ozel anahtar blogu", 0.04, 0.96, 0.55, 0.80, 0.10, "blok"],
  ["baglanti dizesi", 0.10, 0.68, 0.34, 0.72, 0.86, ""],
  ["sozcuk parola", 0.08, 0.62, 0.16, 0.40, 0.34, ""],
  ["ozet degeri", 0.17, 0.00, 0.74, 0.95, 0.12, ""],
  ["sinama yer tutucusu", 0.23, 0.03, 0.28, 0.82, 0.78, ""],
  ["tekil kimlik", 0.18, 0.00, 0.58, 0.86, 0.18, ""],
].map(([ad, pay, sir, e0, e1, ata, bicim]) => ({ ad, pay, sir, e0, e1, ata, bicim }));
const sinifSec = (u) => { let t = 0;
  for (const s of SINIF) { t += s.pay; if (u < t) return s; } return SINIF.at(-1); };

// Her dizi bir islemede girer, bir bolumu sonraki bir islemede calisma agacindan
// cikarilir. Cikarilmak sirri gecersiz kilmaz; tarihcede oldugu yerde durur.
export const DIZILER = Array.from({ length: DIZI }, (_, i) => {
  const s = sinifSec(r());
  const giris = Math.floor(r() * ISLEME), ent = s.e0 + r() * (s.e1 - s.e0);
  const silindi = r() < 0.30 ? giris + 1 + Math.floor(r() * (ISLEME - giris)) : null;
  return { i, sinif: s, sir: r() < s.sir, ent, ata: r() < s.ata, giris, silindi,
    rev: 1 + Math.floor(r() * 9) };
});

export const desenler = (esik) => [
  { ad: "onek bicimi", tut: (d) => d.sinif.bicim === "onek" },
  { ad: "blok basligi", tut: (d) => d.sinif.bicim === "blok" },
  { ad: "anahtar sozcuk atamasi", tut: (d) => d.ata },
  { ad: "entropi esigi", tut: (d) => d.ent >= esik },
];
export const eslerMi = (d, esik) => desenler(esik).some((p) => p.tut(d));

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 sirlar = DIZILER.filter((d) => d.sir);
  console.log(`model (tohum ${TOHUM}): ${ISLEME} isleme, ${DIZI} dizi sabiti, ` +
    `${sirlar.length} tanesi gercekten sir; ${DIZILER.filter((d) => d.silindi).length} dizi ` +
    `calisma agacindan cikarilmis`);
  const y1 = gen([16, 10, 9, 9, 12, 12]);
  console.log("\nentropi esigi degisiyor, oteki uc desen sabit");
  console.log(y1(["entropi esigi", "eslesme", "gercek", "yanlis+", "yanlis+ %", "gorulmeyen"]));
  for (const t of [0.60, 0.80, 1.01]) {
    const e = DIZILER.filter((d) => eslerMi(d, t)), g = e.filter((d) => d.sir).length;
    console.log(y1([t > 1 ? "kapali" : t.toFixed(2), e.length, g, e.length - g,
      (100 * (e.length - g) / e.length).toFixed(1), sirlar.length - g]));
  }

  const ESIK = 0.80, ds = desenler(ESIK);
  const y2 = gen([24, 10, 9, 9, 12]);
  console.log(`\nesik ${ESIK.toFixed(2)}'de desen basina katki`);
  console.log(y2(["desen", "eslesme", "gercek", "yanlis+", "tek kaynak"]));
  for (const p of ds) {
    const l = DIZILER.filter((d) => p.tut(d));
    const tek = l.filter((d) => ds.filter((q) => q.tut(d)).length === 1 && d.sir).length;
    console.log(y2([p.ad, l.length, l.filter((d) => d.sir).length,
      l.filter((d) => !d.sir).length, tek]));
  }

  const y3 = gen([24, 7, 12, 13, 13]);
  console.log(`\nesik ${ESIK.toFixed(2)}'de sinifa gore gorulmeyen sir`);
  console.log(y3(["dizi sinifi", "dizi", "gercek sir", "eslesen sir", "gorulmeyen"]));
  for (const s of SINIF) {
    const l = DIZILER.filter((d) => d.sinif === s), sl = l.filter((d) => d.sir);
    const e = sl.filter((d) => eslerMi(d, ESIK)).length;
    console.log(y3([s.ad, l.length, sl.length, e, sl.length - e]));
  }
}
```

```
model (tohum 20260329): 1800 isleme, 940 dizi sabiti, 310 tanesi gercekten sir; 269 dizi calisma agacindan cikarilmis

entropi esigi degisiyor, oteki uc desen sabit
entropi esigi      eslesme   gercek  yanlis+   yanlis+ %  gorulmeyen
0.60                   855      274      581        68.0          36
0.80                   677      260      417        61.6          50
kapali                 519      245      274        52.8          65

esik 0.80'de desen basina katki
desen                      eslesme   gercek  yanlis+  tek kaynak
onek bicimi                     93       89        4          12
blok basligi                    39       37        2          32
anahtar sozcuk atamasi         442      172      270          86
entropi esigi                  281      105      176          15

esik 0.80'de sinifa gore gorulmeyen sir
dizi sinifi                dizi  gercek sir  eslesen sir   gorulmeyen
onekli erisim anahtari       93          89           89            0
rastgele oturum sirri       102          83           71           12
ozel anahtar blogu           39          37           37            0
baglanti dizesi              89          56           44           12
sozcuk parola                66          39           13           26
ozet degeri                 179           0            0            0
sinama yer tutucusu         214           6            6            0
tekil kimlik                158           0            0            0
```

İlk tablo ayarlanabilir tek düğmeyi sonuna kadar çeviriyor. Entropi eşiği 0,60'ta eşleşme 855 ve
yanlış pozitif oranı yüzde 68,0; desen tamamen kapatıldığında eşleşme 519'a iniyor ama oran ancak
yüzde 52,8'e düşüyor. **Gürültünün üçte ikisi eşiğin dokunmadığı yerdedir.** Kapatmanın bedeli de
aynı satırda: gerçek bulgu 274'ten 245'e iniyor, görülmeyen sır 36'dan 65'e çıkıyor.

İkinci tablo kaynağı adıyla yazıyor. Anahtar sözcük ataması deseni tek başına 442 eşleşme üretiyor
ve 270'i yanlış pozitif — eşik 0,80'deki bütün gürültünün üçte ikisi. Nedeni desenin ne
yaptığındadır: değere değil, değerin atandığı **ada** bakar; adı bir parola alanına benzeyen her
satır eşleşir, içindeki değer bir sınama yer tutucusu olsa da. Önek biçimi deseni 93 eşleşmede 4,
blok başlığı 39 eşleşmede 2 yanlış pozitif veriyor. **Bir desen kümesinin yanlış pozitif oranı
ortalama bir değer değildir; neredeyse tamamı tek bir desenden gelir ve o desen kaldırılmadan oran
düşmez.**

Son sütun kaldırma kararını zorlaştırıyor. Aynı desen 86 gerçek sırrın **tek kaynağıdır**; blok
başlığı 32, entropi eşiği 15, önek biçimi 12 kalemde tek kaynaktır. En gürültülü desen en çok
tekil katkıyı veren desendir, bu yüzden karar kaldırmak değil onun bulgularını ayrı bir kuyruğa
almaktır: 270 yanlış pozitif aynı listeye girdiğinde 86 tekil gerçek bulgu arasında kaybolur.

Üçüncü tablo eşikten bağımsız kör noktayı veriyor. Sözcük parola sınıfının 39 gerçek sırrının
26'sı hiçbir desene takılmıyor: entropisi düşüktür, öneki yoktur ve üçte ikisi bir parola adına
atanmamıştır. Bağlantı dizesi ve rastgele oturum sırrı sınıfları da on ikişer kalem kaçırıyor;
toplam 50 sır **taranmış olduğu hâlde** görülmüyor. Karşıt uç da tabloda: özet değeri ve tekil
kimlik sınıflarında 337 dizi var ve içlerinde tek bir sır yok — ikisi de entropi ölçüsünde
yukarıda durduğu için eşik düşürüldükçe raporu bunlar doldurur.

#### Tarihçenin Bedeli ve Artımlının Kör Noktası

```js
// tarihce.mjs — ayni desen kumesi uc yerlesimde kosuyor: her yapida calisma agaci,
// her yapida artimli fark ve bir kereye mahsus tam tarihce. Sureler MODEL dakikasidir.
import { DIZILER, ISLEME, KALEM_DK, eslerMi } from "./sir.mjs";

const ESIK = 0.80, YAPI = 90, BASLANGIC = 900, INCELEME_DK = 4;
const komut = (y) => BASLANGIC + Math.round((y + 1) * (ISLEME - BASLANGIC) / YAPI);
const canli = (d, c) => d.giris <= c && (d.silindi === null || d.silindi > c);
const SIR = DIZILER.filter((d) => d.sir);
const KOMUTLAR = Array.from({ length: YAPI }, (_, y) => komut(y));
// Yalniz tarihcede duran dizi: hicbir yapi aninda calisma agacinda degil.
const yalnizTarihce = (d) => !KOMUTLAR.some((c) => canli(d, c));

// Bir yerlesim, her yapida hangi kalem kumesinin tarandigini soyler. Calisma agaci
// yerlesimi o andaki dosyalari, artimli yerlesim yalniz onceki yapidan beri gireni
// gorur; tam tarihce kurulus aninda gecmisteki her revizyonu bir kez okur.
const GECMIS = DIZILER.filter((d) => d.giris <= BASLANGIC);
function olc(ad, kume, birKerelik = 0) {
  const gorulen = new Set(), blokeYapi = new Set(), bakilan = new Set();
  let taranan = birKerelik;
  if (birKerelik) for (const d of GECMIS) {
    bakilan.add(d); if (eslerMi(d, ESIK)) gorulen.add(d);
  }
  for (let y = 0; y < YAPI; y++) {
    const liste = kume(komut(y), y ? komut(y - 1) : BASLANGIC);
    taranan += liste.length;
    for (const d of liste) {
      bakilan.add(d);
      if (gorulen.has(d) || !eslerMi(d, ESIK)) continue;
      gorulen.add(d); blokeYapi.add(y);
    }
  }
  const gercek = [...gorulen].filter((d) => d.sir);
  const yanlis = gorulen.size - gercek.length;
  return { ad, taranan, eslesme: gorulen.size, gercek: gercek.length, yanlis,
    bakilmadi: SIR.filter((d) => !bakilan.has(d)).length,
    kacan: SIR.filter((d) => bakilan.has(d) && !gorulen.has(d)).length,
    bloke: blokeYapi.size, dk: Math.round(taranan * KALEM_DK),
    incDk: yanlis * INCELEME_DK,
    silinmis: gercek.filter(yalnizTarihce).length };
}

const REVIZYON = GECMIS.reduce((a, d) => a + d.rev, 0);
const YERLESIM = [
  olc("her yapida calisma agaci", (c) => DIZILER.filter((d) => canli(d, c))),
  olc("her yapida artimli fark",
    (c, o) => DIZILER.filter((d) => d.giris > o && d.giris <= c)),
  olc("tam tarihce + artimli",
    (c, o) => DIZILER.filter((d) => d.giris > o && d.giris <= c), REVIZYON),
];

const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j])
  : String(v).padEnd(B[0]))).join("");
const y = gen([26, 9, 8, 9, 11, 13, 8, 11, 9]);
console.log(`${YAPI} yapi, ${BASLANGIC}. islemede kurulan tarayici, ${SIR.length} gercek sir; ` +
  `tam tarihce ${REVIZYON} dizi revizyonu okur, kalem basina ${KALEM_DK} model dakikasi`);
console.log(y(["yerlesim", "eslesme", "gercek", "yanlis+", "bakilmadi", "desen kacan",
  "bloke", "tarama dk", "inc. dk"]));
for (const o of YERLESIM) {
  console.log(y([o.ad, o.eslesme, o.gercek, o.yanlis, o.bakilmadi, o.kacan, o.bloke,
    o.dk, o.incDk]));
}

const tam = YERLESIM.at(-1);
console.log(`\ntam tarihce bir kez ${Math.round(REVIZYON * KALEM_DK)} dakika alip ` +
  `${tam.gercek} sirri buluyor; ${tam.silinmis} tanesi hicbir yapi aninda calisma agacinda ` +
  `degil, silinmis ama dondurulmedigi surece gecerli`);
console.log(`desen korlugu yerlesimden bagimsizdir: tam tarihcede ${tam.kacan} sir okundugu ` +
  `halde hicbir desene takilmiyor. Pahali olan tarama da degil inceleme: ${tam.dk} dakika ` +
  `tarama karsiliginda ${tam.incDk} dakika, cunku ${tam.eslesme} eslesmenin ${tam.yanlis} ` +
  `tanesi yanlis pozitiftir`);
```

```
90 yapi, 900. islemede kurulan tarayici, 310 gercek sir; tam tarihce 2378 dizi revizyonu okur, kalem basina 0.02 model dakikasi
yerlesim                    eslesme  gercek  yanlis+  bakilmadi  desen kacan   bloke  tarama dk  inc. dk
her yapida calisma agaci        640     246      394         18           46      89       1057     1576
her yapida artimli fark         341     127      214        164           19      89          9      856
tam tarihce + artimli           677     260      417          0           50      89         57     1668

tam tarihce bir kez 48 dakika alip 260 sirri buluyor; 14 tanesi hicbir yapi aninda calisma agacinda degil, silinmis ama dondurulmedigi surece gecerli
desen korlugu yerlesimden bagimsizdir: tam tarihcede 50 sir okundugu halde hicbir desene takilmiyor. Pahali olan tarama da degil inceleme: 57 dakika tarama karsiliginda 1668 dakika, cunku 677 eslesmenin 417 tanesi yanlis pozitiftir
```

Üç satır aynı dört deseni taşıyor; ayrıldıkları yer **neye baktıklarıdır**. En yaygın kurulum
ikinci satırdır: her yapıda yalnız o yapının getirdiği farkı taramak. 9 dakika harcıyor ve
127 sır buluyor. Aynı satırda 164 sırrın **hiç bakılmadığı** yazılı — tarayıcı
kurulmadan önce depoya girmiş kalemler. Artımlı bir denetim onlara asla uğramaz ve her koşumda
temiz rapor verir, çünkü baktığı küme her zaman yenidir. **Bir artımlı taramanın kapsama listesi
kurulduğu işlemedir**; o tarih raporda yazılı değilse sıfır bulgu ile hiç bakılmamış olmak ayırt
edilemez.

Birinci satır bu boşluğu pahalı kapatıyor. Her yapıda çalışma ağacının tamamını taramak 1 057
dakika harcıyor — artımlı yerleşimin yüz on yedi katı. Karşılığında kurulmadan önce girmiş ve hâlâ
duran sırları görüyor: hiç bakılmayan sayısı 164'ten 18'e iniyor. Kalan 18 kalem hiçbir yapı
anında çalışma ağacında değildir. Bloke sütunu üç yerleşimi ayırmıyor: üçü de 90 yapının 89'unu
en az bir kez durduruyor, çünkü bloklayan şey gerçek sır değil, kuyruğa düşen her yeni
eşleşmedir.

Üçüncü satır sıralamayı tersine çeviriyor. Kuruluş anında geçmişin tamamını taramak bir kerelik
2 378 dizi revizyonu okur ve **48 dakika** sürer; çalışma ağacı yerleşiminin harcadığı
1 057 dakikanın yirmi ikide biridir.
Karşılığında hiç bakılmayan sır sıfıra iner, gerçek bulgu 260'a çıkar ve bunların 14'ü hiçbir yapı
anında çalışma ağacında değildir — silinmiş, ama silme döndürme olmadığı için hâlâ geçerli. Bu 14
kalem yalnız bu yerleşimde görünür. Tarihçeyi taramak pahalı bir iş değildir; pahalı olan onu
**her yapıda** yeniden taramaktır.

Son iki sütun bedelin yerini yazıyor. Tam tarihçe yerleşiminde 57 dakika tarama karşılığında
1 668 dakika inceleme çıkıyor: bedelin yüzde 97'si 417 yanlış pozitifin açılıp kapatılmasıdır.
**Bir sır taramasının hattan aldığı sürenin ölçüsü koşum süresi değil, yanlış pozitif oranıdır**
ve o oranın nereden geldiği bir önceki bölümde yazılıydı.

#### Özet

- Ayarlanabilir tek desen olan entropi eşiği yanlış pozitif oranını yüzde 68,0'dan ancak 52,8'e
  indirir ve bu sırada gerçek bulgu 274'ten 245'e düşer.
- Gürültünün kaynağı tek bir desendir: anahtar sözcük ataması 442 eşleşmenin 270'ini yanlış
  çıkarır, çünkü değere değil değerin atandığı ada bakar. Aynı desen 86 gerçek sırrın tek
  kaynağıdır, bu yüzden karar kaldırmak değil ayrı bir kuyruğa almaktır.
- 310 sırrın 50'si taranmış olduğu hâlde hiçbir desene takılmaz; 26'sı tek bir sınıftadır — düşük
  entropili, öneksiz ve bir parola adına atanmamış sözcük parolalar.
- Üç yerleşim üç ayrı kör nokta taşır: artımlı fark 9 dakikada koşar ama kurulmadan önceki 164
  sırrı hiç görmez, her yapıda çalışma ağacı 1 057 dakika harcar ve 18'ini görmez, bir kerelik tam
  tarihçe 48 dakikada geçmişin tamamını okur ve yalnız orada duran 14 sırrı ortaya çıkarır.
- Bedelin yüzde 97'si tarama değil incelemedir: 57 dakika koşum karşılığında 417 yanlış pozitif
  için 1 668 dakika harcanır.

#### Sonraki Adım

Dört denetim de aynı yerde durdu: depodaki dosyalar. Oysa üretime giden şey dosya değil bir
**imajdır** — derlenmiş çıktı, gereken kitaplıklar ve altlarındaki işletim katmanı tek bir yapıda
birleşir. Bu yapının açıkları iki ayrı yerden gelir ve düzeltme yolları benzemez: bir bölümü
ekibin eklediği katmanlardadır, bir bölümü hiç dokunulmamış temel katmandan miras alınır. İkincisi
için yazılacak tek satır kod yoktur; kapatan şey bir yükseltmedir. Sonraki ders bir imaj katman
listesinde bu iki kaynağı ayırır ve temel katmanı yükseltmenin açıkların kaçını kapattığını
sayar.

### Konteyner İmaj Tarama

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/konteyner-imaj-tarama

348 bileşenlik bir imajın 144 bulgusu kaynağına göre ayrılıyor: 75'i temel katmandan, 69'u uygulama katmanından gelir ve temel imajı yeni sürüme almak tek işlemde 64 bulguyu, temel kaynaklı olanların yüzde 85,3'ünü kapatırken uygulama katmanına hiç dokunmaz; 18 açık ise paket kaydında olmayan 31 bileşende durduğu için hiçbir koşumda görünmez. Üç imajın bulgu satırı toplamı 269, tekil açık 144. Her ağır bulguyu bloklayan politika 60 yapının 49'unu durdurur ve 12'sinde duran bulgunun tamamı ekibin düzeltemeyeceği temel katmandadır; blokajı daraltmak ağır temel maruziyetini 940 yapı-bulguda bırakır, yirmi yapıda bir temel tazelemesi 382'ye indirir.

Depodaki dosyalar dört denetimden geçti. Üretime giden şey ise bir **imajdır** ve içinde ekibin
hiç yazmadığı, hiç seçmediği bir katman yığını taşır. **Konteyner imaj tarama**, bu yığındaki
bileşenleri çıkarıp bilinen açık kataloğuyla karşılaştıran denetimdir; eşleştirme mekaniği
bağımlılık bileşen analiziyle aynıdır, farklı olan **girdinin yapısıdır**.

Bir imajda bulunan açığın tek bir ciddiyet değeri vardır ama iki ayrı düzeltme yolu. Bir bölümü
uygulamanın eklediği katmanlardadır ve o katmanı üreten ekip düzeltebilir. Bir bölümü hiç
dokunulmamış temel katmandan miras alınır; onun için yazılacak kod yoktur, kapatan şey temel
imajın sürümünü değiştirmektir. Bu ders bulgu kümesini bu eksende ayırır ve ayrımın hatta ne
anlama geldiğini sayar.

- **OD24.** Bir imaj katman listesidir; alttaki katmanlar temel imajdan gelir, üsttekiler
  uygulamanın ekledikleridir. Katman mekaniği bu kursta **girdidir**, tekrar kurulmaz. Aşağıdaki
  katman adları, bileşen sayıları ve açıklar **kurgudur**.
- **OD25.** Bir bulgunun **kaynağı**, açığı taşıyan bileşenin bulunduğu katmandır. Bu alan tarama
  çıktısında ayrı bir sütundur ve düzeltme yolunu belirler.
- **OD26.** Tarama envanterden okur. Paket kaydında görünmeyen bir bileşen — elle yerleştirilmiş
  bir ikili — envantere hiç girmez ve taşıdığı açık hiçbir koşumda görünmez.
- **OD27.** Temel katmandaki bir açığın **yeni temel sürümde kapalı** olup olmadığı ayrı bir
  alandır. Uygulama katmanındaki açık temel yükseltmesinden hiç etkilenmez.
- **OD28.** Katalog kayıtları zamanla yayımlanır: imaj hiç değişmese bile bilinen bulgu sayısı
  artar.
- **OD29.** Yapı başına 1,5, bloke yapı başına 35 model dakikası. **Maruziyet** yapı-bulgu
  birimindedir: her yapıda imajda duran ağır temel açık sayısının yapılar boyunca toplamı.
  Süreler **veridir**, ölçüm değildir.

#### Açığın Kaynağı ve Yükseltmenin Kapattığı

```js
// imaj.mjs — bir imajin katman listesi taraniyor; olculen sey acigin temel katmandan mi
// uygulama katmanindan mi geldigi ve temel yukseltmesinin kacini kapattigidir. MODELDIR.
export const TOHUM = 20260404, TARAMA_DK = 1.5;
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"];
const AGIR = new Set(["yuksek", "kritik"]);

// Katmanlar alttan uste: ilk ikisi temel imajdan gelir, ustteki ucu uygulamanin
// ekledikleridir. Katman adlari ve bilesen sayilari kurgudur.
export const KATMAN = [
  ["isletim taban", "temel", 96],
  ["calisma zamani", "temel", 64],
  ["bagimliliklar", "uygulama", 148],
  ["derlenmis cikti", "uygulama", 22],
  ["yardimci araclar", "uygulama", 18],
].map(([ad, kaynak, sayi]) => ({ ad, kaynak, sayi }));

// envanterde: bilesenin paket yoneticisi kaydinda gorunup gorunmedigi. Kayitta
// olmayan bilesen (elle kopyalanmis ikili) tarayicinin envanterine hic girmez.
// yeniTemelde: acigin, temel imajin yeni surumunde kapali olup olmadigi.
export const BILESEN = [];
for (const k of KATMAN) for (let i = 0; i < k.sayi; i++) {
  const envanterde = r() < (k.kaynak === "temel" ? 0.98 : 0.86), acik = [];
  const n = r() < 0.26 ? 1 + Math.floor(r() * 2.4) : 0;
  for (let j = 0; j < n; j++) acik.push({
    ciddiyet: CIDDIYET[Math.floor(r() * 4)],
    yeniTemelde: k.kaynak === "temel" && r() < 0.84, duzeltmeVar: r() < 0.72 });
  BILESEN.push({ katman: k, kaynak: k.kaynak, envanterde, acik });
}

// Tarama envanterden okur: kayitta olmayan bilesenin acigi hicbir kosumda gorunmez.
export function tara(yeniTemel = false) {
  const bulgu = [], gorulmeyen = [], kapanan = [];
  for (const b of BILESEN) for (const a of b.acik) {
    const kayit = { ...a, kaynak: b.kaynak, katman: b.katman.ad };
    if (!b.envanterde) gorulmeyen.push(kayit);
    else if (yeniTemel && a.yeniTemelde) kapanan.push(kayit);
    else bulgu.push(kayit);
  }
  return { bulgu, gorulmeyen, kapanan };
}

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 { bulgu, gorulmeyen } = tara();
  console.log(`model (tohum ${TOHUM}): ${BILESEN.length} bilesen, ` +
    `${BILESEN.reduce((a, b) => a + b.acik.length, 0)} acik; ` +
    `${BILESEN.filter((b) => !b.envanterde).length} bilesen paket kaydinda yok`);
  const y1 = gen([18, 11, 10, 14, 8, 9, 18]);
  console.log("\n" + y1(["katman", "kaynak", "bilesen", "kayitta yok", "acik", "bulgu",
    "gorulmeyen acik"]));
  for (const k of KATMAN) {
    const l = BILESEN.filter((b) => b.katman === k);
    const ac = l.reduce((a, b) => a + b.acik.length, 0);
    const gor = l.filter((b) => !b.envanterde).reduce((a, b) => a + b.acik.length, 0);
    console.log(y1([k.ad, k.kaynak, k.sayi, l.filter((b) => !b.envanterde).length,
      ac, ac - gor, gor]));
  }

  const once = tara(), sonra = tara(true);
  const y2 = gen([12, 9, 8, 13, 12, 11, 12]);
  console.log("\ntemel imaj yeni surume alindiginda");
  console.log(y2(["kaynak", "bulgu", "agir", "yeni bulgu", "yeni agir", "kapanan",
    "kapanan %"]));
  for (const kay of ["temel", "uygulama"]) {
    const o = once.bulgu.filter((a) => a.kaynak === kay);
    const s = sonra.bulgu.filter((a) => a.kaynak === kay);
    console.log(y2([kay, o.length, o.filter((a) => AGIR.has(a.ciddiyet)).length, s.length,
      s.filter((a) => AGIR.has(a.ciddiyet)).length, o.length - s.length,
      (100 * (o.length - s.length) / o.length).toFixed(1)]));
  }
  const kalanAgir = sonra.bulgu.filter((a) => AGIR.has(a.ciddiyet));
  console.log(`\nyukseltme ${once.bulgu.length - sonra.bulgu.length} bulguyu tek islemde ` +
    `kapatiyor, ${sonra.bulgu.length} bulgu kaliyor; kalan ${kalanAgir.length} agir bulgunun ` +
    `${kalanAgir.filter((a) => a.kaynak === "uygulama").length} tanesi uygulama katmanindadir`);
  console.log(`kalan agir bulgunun ${kalanAgir.filter((a) => !a.duzeltmeVar).length} tanesinin ` +
    `yayimlanmis bir duzeltmesi yok; ${gorulmeyen.length} acik ise hicbir kosumda gorunmuyor, ` +
    `cunku tasidigi bilesen paket kaydinda hic yer almiyor`);
}
```

```
model (tohum 20260404): 348 bilesen, 162 acik; 31 bilesen paket kaydinda yok

katman                 kaynak   bilesen   kayitta yok    acik    bulgu   gorulmeyen acik
isletim taban           temel        96             2      43       41                 2
calisma zamani          temel        64             3      36       34                 2
bagimliliklar        uygulama       148            21      64       50                14
derlenmis cikti      uygulama        22             2       8        8                 0
yardimci araclar     uygulama        18             3      11       11                 0

temel imaj yeni surume alindiginda
kaynak          bulgu    agir   yeni bulgu   yeni agir    kapanan   kapanan %
temel              75      33           11           6         64        85.3
uygulama           69      39           69          39          0         0.0

yukseltme 64 bulguyu tek islemde kapatiyor, 80 bulgu kaliyor; kalan 45 agir bulgunun 39 tanesi uygulama katmanindadir
kalan agir bulgunun 10 tanesinin yayimlanmis bir duzeltmesi yok; 18 acik ise hicbir kosumda gorunmuyor, cunku tasidigi bilesen paket kaydinda hic yer almiyor
```

İlk tablo bulgu kümesini katmana göre bölüyor. 348 bileşenin 160'ı temel katmanlardadır ve
79 açık taşır; 188'i uygulamanın eklediği katmanlardadır ve 83 açık taşır. Sayılar birbirine
yakındır, ama bu iki yarının hattaki karşılığı aynı değildir. Temel katmandaki 75 bulgunun
düzeltmesi imajı kuran ekibin elinde değildir; o katmanı başkası üretmiştir ve içindeki paketleri
tek tek değiştirmek imajın kimliğini bozar.

Aynı tablonun son iki sütunu tarayıcının kendi sınırını yazıyor. 31 bileşen paket kaydında hiç
görünmüyor ve bunların 21'i bağımlılıklar katmanındadır: derleme sırasında elle yerleştirilmiş,
bir paket yöneticisinden geçmemiş ikililer. Taşıdıkları 18 açık **hiçbir koşumda** raporlanmıyor.
Bu sayı bir eşik ayarıyla değişmez; kaynağı envanterin nasıl kurulduğudur. **Bir imaj taramasının
kapsama listesi, kaç bileşeni envanterin tanıdığıdır** ve bu sayı raporlanmadıkça temiz görünen
bir katmanın gerçekten temiz mi yoksa okunmamış mı olduğu bilinmez.

İkinci tablo ayrımın karşılığını veriyor. Temel imajı yeni sürüme almak **tek bir işlemde** 64
bulguyu kapatıyor: temel kaynaklı bulguların yüzde 85,3'ü. Aynı işlem uygulama katmanına hiç
dokunmuyor — 69 bulgu olduğu gibi kalıyor, kapanan sıfır. Bu, ciddiyet alanının hiç taşımadığı
bir bilgidir: aynı ağırlıktaki iki kalemden biri bir satırlık sürüm değişikliğiyle, diğeri ayrı
ayrı ele alınacak onlarca değişiklikle kapanır.

Yükseltmeden sonra kalan 45 ağır bulgunun 39'u uygulama katmanındadır ve 10'unun yayımlanmış bir
düzeltmesi yoktur. Yani temel imaj yükseltmesi ağır kalemlerin çoğunu **taşımaz**; ağır bulgunun
büyük bölümü zaten ekibin kendi eklediği katmanlardaydı. İki cümlenin birlikte okunması gerekir:
yükseltme bulgu sayısını hızla düşürür, ama düşürdüğü bulguların ciddiyet dağılımı kalanlardan
farklıdır.

#### Aynı Bulgu Kaç Kez Sayılıyor

```js
// hat.mjs — ayni temel imajdan uretilen uc imaj 60 yapi boyunca taraniyor; olculen sey
// ayni bulgunun kac imajda tekrar sayildigi ve blokajin kime dustugudur. MODELDIR.
import { tara, TARAMA_DK } from "./imaj.mjs";

const YAPI = 60, TAZELEME = 20, BLOKE_DK = 35;
const AGIR = new Set(["yuksek", "kritik"]);
// yayim: kaydin yayimlandigi yapi. Imaj hic degismese bile bulgu sayisi artar,
// cunku yeni kayitlar zamanla yayimlanir.
const damga = tara().bulgu.map((a, i) => ({ ...a, yayim: (i * 37) % YAPI }));
const TEMEL = damga.filter((a) => a.kaynak === "temel");
const UYGULAMA = damga.filter((a) => a.kaynak === "uygulama");

// Iki temel imaj: t-a tam bilesen kumesini, t-b daha dar bir tabani tasir.
const TABAN = { "t-a": TEMEL, "t-b": TEMEL.filter((_, i) => i % 3 !== 0) };
const IMAJ = [
  { ad: "abone portali", taban: "t-a", uyg: UYGULAMA.filter((_, i) => i % 3 === 0) },
  { ad: "sayac alim", taban: "t-a", uyg: UYGULAMA.filter((_, i) => i % 3 === 1) },
  { ad: "yonetim araci", taban: "t-b", uyg: UYGULAMA.filter((_, i) => i % 3 === 2) },
];

const POLITIKA = [
  { ad: "her agir bulgu", blok: (a) => AGIR.has(a.ciddiyet), tazele: false },
  { ad: "yalniz uygulama katmani", blok: (a) => AGIR.has(a.ciddiyet) &&
    a.kaynak === "uygulama", tazele: false },
  { ad: "uygulama + temel tazeleme", blok: (a) => AGIR.has(a.ciddiyet) &&
    a.kaynak === "uygulama", tazele: true },
];

function olc(p) {
  const gorulen = new Set(), kapali = new Set();
  let bloke = 0, cozemez = 0, maruziyet = 0, bloklayan = 0;
  for (let y = 0; y < YAPI; y++) {
    const im = IMAJ[y % IMAJ.length];
    if (p.tazele && y > 0 && y % TAZELEME === 0)
      for (const a of TEMEL) if (a.yeniTemelde && a.yayim <= y) kapali.add(a);
    const kume = [...TABAN[im.taban], ...im.uyg]
      .filter((a) => a.yayim <= y && !kapali.has(a));
    const yeni = kume.filter((a) => p.blok(a) && !gorulen.has(a));
    for (const a of yeni) gorulen.add(a);
    if (yeni.length) {
      bloke++; bloklayan += yeni.length;
      if (yeni.every((a) => a.kaynak === "temel")) cozemez++;
    }
    maruziyet += kume.filter((a) => a.kaynak === "temel" && AGIR.has(a.ciddiyet)).length;
  }
  return { ad: p.ad, bloklayan, bloke, cozemez, maruziyet,
    dk: Math.round(YAPI * TARAMA_DK + bloke * BLOKE_DK) };
}

const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j])
  : String(v).padEnd(B[0]))).join("");

const ham = IMAJ.reduce((a, im) => a + TABAN[im.taban].length + im.uyg.length, 0);
const tekil = new Set(IMAJ.flatMap((im) => [...TABAN[im.taban], ...im.uyg])).size;
console.log(`${IMAJ.length} imaj, ${YAPI} yapi; uc imajin bulgu satiri toplami ${ham}, ` +
  `tekil acik ${tekil}. Fark temel katmanin her imajda yeniden sayilmasidir`);
const y = gen([28, 12, 9, 14, 16, 11]);
console.log("\n" + y(["politika", "bloklayan", "bloke", "ekip cozemez", "agir maruziyet",
  "toplam dk"]));
for (const p of POLITIKA) {
  const o = olc(p);
  console.log(y([o.ad, o.bloklayan, o.bloke, o.cozemez, o.maruziyet, o.dk]));
}

const [hepsi, uyg, taze] = POLITIKA.map(olc);
console.log(`\nher agir bulguyu bloklayan politika ${hepsi.bloke} yapiyi durduruyor ve ` +
  `${hepsi.cozemez} tanesinde duran bulgunun tamami temel katmandadir: imaji kuran ekibin ` +
  `yazabilecegi tek satir kod yoktur`);
console.log(`blokaji uygulama katmanina daraltmak duran yapiyi ${uyg.bloke}'ye indiriyor ama ` +
  `agir temel maruziyetini ${uyg.maruziyet} yapi-bulgu olarak yerinde birakiyor`);
console.log(`${TAZELEME} yapida bir temel tazelemesi ayni blokajla maruziyeti ` +
  `${taze.maruziyet} yapi-bulguya, yuzde ` +
  `${(100 * (1 - taze.maruziyet / uyg.maruziyet)).toFixed(1)} daha aza indiriyor`);
```

```
3 imaj, 60 yapi; uc imajin bulgu satiri toplami 269, tekil acik 144. Fark temel katmanin her imajda yeniden sayilmasidir

politika                       bloklayan    bloke  ekip cozemez  agir maruziyet  toplam dk
her agir bulgu                        72       49            12             940       1805
yalniz uygulama katmani               39       37             0             940       1385
uygulama + temel tazeleme             39       37             0             382       1385

her agir bulguyu bloklayan politika 49 yapiyi durduruyor ve 12 tanesinde duran bulgunun tamami temel katmandadir: imaji kuran ekibin yazabilecegi tek satir kod yoktur
blokaji uygulama katmanina daraltmak duran yapiyi 37'ye indiriyor ama agir temel maruziyetini 940 yapi-bulgu olarak yerinde birakiyor
20 yapida bir temel tazelemesi ayni blokajla maruziyeti 382 yapi-bulguya, yuzde 59.4 daha aza indiriyor
```

İlk satır bir sayma hatasının kaynağını gösteriyor. Üç imajın raporları alt alta konduğunda 269
bulgu satırı çıkıyor, ama tekil açık sayısı 144. Aradaki 125 satır aynı temel katmanın iki imajda
yeniden sayılmasıdır. **Bir kuruluşun toplam bulgu sayısı, imaj sayısıyla çarpılmış bir
büyüklüktür**; iki katı imaj üretmek aynı açıkları iki katı kez raporlar ve rakam kötüleşirken
altındaki durum değişmez. Aynı çarpan tersine de çalışır: tek bir temel imaj yükseltmesi bütün
imajlarda aynı anda kapanır.

Politika tablosunun ilk satırı hattın en yaygın kurulumudur: ağır her bulgu yapıyı durdurur.
60 yapının 49'u duruyor ve bunların **12'sinde durduran bulguların tamamı temel katmandadır**.
O on iki yapıda geliştiriciye düşen bir iş yoktur; bulgu doğrudur, ciddiyeti doğrudur, ama
kapatacak değişiklik başka bir deponun sorumluluğundadır. Bu, blokajın en pahalı biçimidir:
duran yapı, düzeltmesi kendisinde olmayan bir ekibin önünde durur.

İkinci satır blokajı uygulama katmanına daraltıyor. Duran yapı 49'dan 37'ye, toplam bedel
1 805 dakikadan 1 385 dakikaya iniyor ve ekibin çözemeyeceği blokaj sıfırlanıyor. Ama maruziyet
sütunu **hiç değişmiyor**: her iki politikada da 940 yapı-bulgu. Bu sütun asıl soruyu yazıyor —
temel katmandaki ağır açıklar altmış yapı boyunca üretime gitmeye devam ediyor. Yapıyı durdurmak
onları kapatmıyordu; yalnız birinin dikkatini çekiyordu.

Üçüncü satır kapatanı gösteriyor. Yirmi yapıda bir temel imajı tazelemek, aynı blokaj ve aynı
1 385 dakika ile maruziyeti 382 yapı-bulguya, yüzde 59,4 daha aza indiriyor. **Temel katman
açığını kapatan şey bir kapı değil, bir tazeleme ritmidir.** Kapı yalnız bulguyu görünür kılar;
imajın hangi sıklıkla yeniden kurulduğu ise onu gerçekten kaldırır. İmaj hiç değişmese bile
bilinen bulgu sayısının artması bunu zorunlu kılar: yeni kayıtlar yayımlanmaya devam eder ve
kurulduğu gün temiz raporlanan bir imaj, hiç dokunulmadan aylar sonra ağır bulgular taşır.

#### Özet

- Aynı bulgu kümesi kaynağına göre bölünür: 144 bulgunun 75'i temel katmandan, 69'u uygulamanın
  eklediği katmanlardan gelir ve iki yarının düzeltme yolu birbirine benzemez.
- Temel imajı yeni sürüme almak tek işlemde 64 bulguyu — temel kaynaklıların yüzde 85,3'ünü —
  kapatır ve uygulama katmanına hiç dokunmaz; kapanan oran uygulama tarafında sıfırdır.
- Yükseltmeden sonra kalan 45 ağır bulgunun 39'u uygulama katmanındadır: yükseltme bulgu sayısını
  hızla düşürür ama ağır kalemlerin çoğunu yerinde bırakır.
- 31 bileşen paket kaydında görünmez ve taşıdığı 18 açık hiçbir koşumda raporlanmaz; bu sayı eşikle
  değişmez, kaynağı envanterin nasıl kurulduğudur.
- Üç imajın 269 bulgu satırı 144 tekil açığa karşılık gelir; bulgu sayısı imaj sayısıyla çarpılan
  bir büyüklüktür ve tek bir temel yükseltmesi bütün imajlarda aynı anda kapanır.
- Blokaj maruziyeti düşürmez: her ağır bulguyu bloklamak 49 yapı durdurur ve 12'sinde ekibin
  yapabileceği bir iş yoktur, ama ağır temel maruziyeti 940 yapı-bulguda kalır; yirmi yapıda bir
  tazeleme aynı blokajla onu 382'ye indirir.

#### Sonraki Adım

İmaj bir yerden gelir: onu üreten ve çalıştıran ortamın kendisi de yazılmış bir metindir. Kaç
kopya koşacağı, hangi ağ kuralının açık kalacağı, hangi deponun dışarıdan okunabilir olacağı bir
şablonda tanımlıdır ve bu şablon da depoda durur. Buradaki denetimlerin hiçbiri ona bakmadı; oysa
şablondaki tek bir alan, imajın içindeki bütün bulgulardan daha geniş bir yol açabilir. Şablon
denetiminin ayırt edici yanı zamanlamasıdır: dağıtımdan **önce** koşabilen bir kural, kaynağı
oluşmadan yanlış yapılandırmayı durdurur. Sonraki ders bir şablon kümesini kural setinden geçirir,
dağıtımdan önce yakalanan kalemi sayar ve kaç yapının bu yüzden bloke olduğunu ölçer.

### Kod Olarak Altyapı Taraması

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/kod-olarak-altyapi-taramasi

420 şablon kaleminde kuralın dağıtımdan önce verebildiği karar sayılıyor: 100 gerçek ihlalin 55'i kesin olarak yakalanır, 26'sı değeri dağıtım anında hesaplandığı için belirlenemez kalır, 19'u hiç kural yazılmamış alan sınıfındadır. Belirlenemezi geçirmek 26 ihlali görmeden bırakır, durdurmak 65 uygun kalemi boşuna bloklar. Aynı kural seti 60 yapı boyunca dört yerleşimde koşar: kesin kapı 34 yapı durdurup 1 339 yapı-kalem maruziyet bırakır, yalnız dağıtımdan sonra tarayan denetim hiçbir yapıyı durdurmadan 979'a iner, ikisi birlikte 697'ye; her yerleşimde aynı 19 ihlal hiç kapanmaz.

Önceki beş denetim çalıştırılabilir metne bakıyordu: kaynak kodu, çalışan uç, bağımlılık
envanteri, depo geçmişi, imaj katmanı. Depoda bir metin daha durur ve hiçbiri onu okumadı — imajın
**nerede, kaç kopya, hangi ağ kuralıyla ve hangi yetkiyle** koşacağını tanımlayan şablonlar.
Buradaki tek bir alan, imajın içindeki bütün bulgulardan daha geniş bir yol açabilir.

**Kod olarak altyapı taraması**, bu şablonları dağıtımdan önce bir kural setinden geçiren
denetimdir. Ayırt edici yanı zamanlamasıdır: kural, kaynak henüz oluşmadan koşar ve durdurduğu
kalemde ortada düzeltilecek bir kaynak bile olmaz. Ama bu zamanlamanın kendi sınırı vardır ve bu
ders onu sayar — şablon metni her zaman ne olacağını söylemez.

- **OD30.** Kaynak tanımlarının metin olarak yazılması bu kursta **girdidir**; şablon yazımı ve
  kural yazımı tekrarlanmaz. Aşağıdaki 420 kalem, beş şablondaki alanların **kurgu** bir
  modelidir. Bir **kalem** bir kaynak tanımındaki bir alandır.
- **OD31.** Her alan sınıfı için yazılmış bir kural yoktur. Kuralsız kalem hiçbir yerleşimde
  değerlendirilmez ve bu sayı ayarla değişmez.
- **OD32.** Bir alanın değeri her zaman şablon metninden okunamaz: bir bölümü dağıtım anında
  hesaplanır. Okunamayan alanda kural kesin karar veremez; sonuç **belirlenemez**dir.
- **OD33.** Değeri okunabilen alanda kural kesin karar verir. Bu denetimin okunabilir kalemde
  yanlış pozitifi yoktur; belirsizliğin tek kaynağı okunamayan değerdir.
- **OD34.** Dağıtımdan önce durdurulan kalemde kaynak hiç oluşmaz. Dağıtımdan sonra bulunan
  kalemde kaynak vardır ve düzeltmesi bir dağıtım daha ister.
- **OD35.** Şablon taraması yapı başına 2, çalışan kaynak denetimi koşum başına 9, bloke yapı
  başına 20 model dakikası. **Maruziyet** yapı-kalem birimindedir. Süreler **veridir**.

#### Kesin, Belirlenemez ve Kuralsız

```js
// sablon.mjs — kaynak tanimi sablonlari kural setinden geciriliyor; olculen sey
// dagitimdan once kesin karar verilebilen kalem ile belirlenemeyen kalemdir. MODELDIR.
export const TOHUM = 20260411, KALEM = 420;
const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };
const r = uretec(TOHUM);

// Her kalem bir kaynak tanimindaki bir alandir. kuralVar: o alan sinifi icin yazilmis
// bir kural olup olmadigi. okunur: alanin degerinin sablon metninden okunabilme
// olasiligi — sabit yazilmis deger okunur, dagitimda uretilen deger okunmaz.
export const SINIF = [
  ["ag erisim kurali", 0.18, 0.26, 0.95, 0.86],
  ["depo gorunurlugu", 0.12, 0.31, 0.95, 0.62],
  ["kimlik yetkisi", 0.16, 0.34, 0.95, 0.41],
  ["sifreleme ayari", 0.14, 0.19, 0.95, 0.78],
  ["kayit toplama", 0.11, 0.22, 0.95, 0.70],
  ["kaynak siniri", 0.15, 0.15, 0.00, 0.88],
  ["etiket ve sahiplik", 0.14, 0.11, 0.00, 0.93],
].map(([ad, pay, ihlal, kural, okunur]) => ({ ad, pay, ihlal, kural, okunur }));
const sec = (u) => { let t = 0;
  for (const s of SINIF) { t += s.pay; if (u < t) return s; } return SINIF.at(-1); };

export const KALEMLER = Array.from({ length: KALEM }, (_, i) => {
  const s = sec(r());
  return { i, sinif: s, ihlal: r() < s.ihlal, okunur: r() < s.okunur,
    kuralVar: r() < s.kural, dogus: Math.floor(r() * 60) };
});

// Dagitimdan once verilen karar: kural yoksa sessiz, deger okunamiyorsa belirlenemez.
export const karar = (k) => !k.kuralVar ? "kuralsiz"
  : !k.okunur ? "belirlenemez" : k.ihlal ? "ihlal" : "uygun";

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 ihlaller = KALEMLER.filter((k) => k.ihlal);
  console.log(`model (tohum ${TOHUM}): ${KALEM} sablon kalemi, ${ihlaller.length} tanesi ` +
    `gercek ihlal; ${KALEMLER.filter((k) => !k.kuralVar).length} kalem icin yazilmis kural yok`);
  const y1 = gen([20, 8, 8, 11, 9, 15, 13]);
  console.log("\n" + y1(["alan sinifi", "kalem", "ihlal", "kuralsiz", "ihlal+", "belirlenemez",
    "bel. ihlal"]));
  for (const s of SINIF) {
    const l = KALEMLER.filter((k) => k.sinif === s);
    const b = l.filter((k) => karar(k) === "belirlenemez");
    console.log(y1([s.ad, l.length, l.filter((k) => k.ihlal).length,
      l.filter((k) => karar(k) === "kuralsiz").length,
      l.filter((k) => karar(k) === "ihlal").length, b.length,
      b.filter((k) => k.ihlal).length]));
  }

  const bel = KALEMLER.filter((k) => karar(k) === "belirlenemez");
  const kuralsiz = KALEMLER.filter((k) => karar(k) === "kuralsiz");
  const kesinIhlal = KALEMLER.filter((k) => karar(k) === "ihlal");
  const y2 = gen([26, 10, 9, 12, 14]);
  console.log("\nbelirlenemez kalem iki ayri sekilde ele alinabilir");
  console.log(y2(["ele alis", "bulgu", "gercek", "yanlis+", "kacan ihlal"]));
  for (const [ad, kume] of [["belirlenemez gecer", kesinIhlal],
    ["belirlenemez durdurur", [...kesinIhlal, ...bel]]]) {
    const g = kume.filter((k) => k.ihlal).length;
    console.log(y2([ad, kume.length, g, kume.length - g, ihlaller.length - g]));
  }
  console.log(`\ngecirme secildiginde ${bel.filter((k) => k.ihlal).length} gercek ihlal ` +
    `dagitimdan once gorulmuyor; durdurma secildiginde ${bel.filter((k) => !k.ihlal).length} ` +
    `uygun kalem bosuna durduruluyor. Iki secenegin arasinda ucuncu bir deger yok`);
  console.log(`her iki secenekte de ${kuralsiz.filter((k) => k.ihlal).length} ihlal kural ` +
    `yazilmadigi icin hic degerlendirilmiyor; bu sayi ele alisla degismez`);
}
```

```
model (tohum 20260411): 420 sablon kalemi, 100 tanesi gercek ihlal; 132 kalem icin yazilmis kural yok

alan sinifi            kalem   ihlal   kuralsiz   ihlal+   belirlenemez   bel. ihlal
ag erisim kurali          85      22          3       18              8            3
depo gorunurlugu          48      16          1        8             22            8
kimlik yetkisi            66      21          3       10             39           11
sifreleme ayari           57      18          2       15             15            3
kayit toplama             44       5          3        4              7            1
kaynak siniri             59      10         59        0              0            0
etiket ve sahiplik        61       8         61        0              0            0

belirlenemez kalem iki ayri sekilde ele alinabilir
ele alis                       bulgu   gercek     yanlis+   kacan ihlal
belirlenemez gecer                55       55           0            45
belirlenemez durdurur            146       81          65            19

gecirme secildiginde 26 gercek ihlal dagitimdan once gorulmuyor; durdurma secildiginde 65 uygun kalem bosuna durduruluyor. Iki secenegin arasinda ucuncu bir deger yok
her iki secenekte de 19 ihlal kural yazilmadigi icin hic degerlendirilmiyor; bu sayi ele alisla degismez
```

İkinci tablonun ilk satırı bu denetimi öncekilerden ayıran şeyi yazıyor: 55 bulgunun 55'i
gerçektir, yanlış pozitif sıfırdır. Statik çözümleme bir kod konumunun tehlikeli olup olmadığını
tahmin ediyordu; burada kural bir alanın yazılı değerini okur ve karşılaştırır. Değeri okunabilen
kalemde tahmin yoktur. **Bu denetimin belirsizliği bulgu üretme biçiminden değil, okuyamadığı
alandan gelir.**

O alan ilk tablonun altıncı sütununda duruyor. 420 kalemin 91'i belirlenemez: değeri dağıtım
anında hesaplandığı için kural karar veremiyor. Dağılım sınıflara göre keskin biçimde ayrışıyor.
Kimlik yetkisi sınıfının 66 kaleminin 39'u belirlenemez — yetkinin kime verildiği çoğunlukla
dağıtımda çözülen bir başvurudur. Ağ erişim kuralı sınıfında aynı sayı 85 kalemde yalnız 8'dir,
çünkü adres aralıkları şablona yazılıdır. **Şablon taramasının kapsaması alan sınıfına göre
değişir ve bu değişkenliğin kaynağı kural setinin olgunluğu değil, değerin ne zaman
belirlendiğidir.**

İkinci tablo bu belirsizliğin iki ucunu ölçüyor ve arada bir seçenek yok. Belirlenemez kalemi
geçirmek 55 kesin bulguyla kapanır ve 26 gerçek ihlali dağıtımdan önce hiç göstermez. Durdurmak
bulguyu 146'ya çıkarır, 81 gerçek ihlali yakalar, ama 65 uygun kalemi boşuna bloklar — raporun
yüzde 44,5'i yanlış pozitife dönüşür. Bu kursun ilk dersindeki eşik takasının aynısıdır, ama
düğme burada bir sayı değil bir varsayımdır: okunamayan değer iyi mi kabul edilecek, kötü mü.

Son sayı ikisinin de dışında. 132 kalem için yazılmış bir kural yoktur ve bunların 19'u gerçek
ihlaldir. Kaynak sınırı ile etiket ve sahiplik sınıflarında kural seti hiç bakmaz. Bu 19 kalem
belirlenemez de değildir — değerleri okunabilir durumdadır, yalnız soran yoktur. **Bir şablon
taramasının raporunda "ihlal yok" yazan alan sınıfı, ya uygun olan ya da hiç sorulmamış olandır.**

#### Kapı mı, Sonraki Koşum mu

```js
// yerlesim.mjs — ayni kural seti dagitimdan once ve sonra kosuyor; olculen sey bloke
// edilen yapi ile kaynak olustuktan sonra gecen maruziyettir. Sureler MODELDIR.
import { KALEMLER, karar } from "./sablon.mjs";

const YAPI = 60, DENETIM = 5, DUZELTME = 3;
const SABLON_DK = 2, CALISAN_DK = 9, BLOKE_DK = 20;

// kapi: dagitimdan once neyi durdurur. Durdurulan kalemde kaynak hic olusmaz.
// sonra: calisan kaynagi okuyan denetim; orada belirsiz deger yoktur, ama kalem
// zaten uretimdedir ve duzeltmesi bir dagitim daha ister.
function olc(ad, kapi, sonra, sablonTarar) {
  const durdurulan = new Map(), uretimde = new Map();
  for (const k of KALEMLER) {
    if (kapi(k)) durdurulan.set(k.dogus, [...(durdurulan.get(k.dogus) || []), k]);
    else if (k.ihlal) uretimde.set(k, null);
  }
  const bloke = durdurulan.size;
  const yanlisBloke = [...durdurulan.values()].filter((l) => !l.some((k) => k.ihlal)).length;
  let maruziyet = 0, kosum = 0;
  for (let y = 0; y < YAPI; y++) {
    if (sonra && y % DENETIM === DENETIM - 1) {
      kosum++;
      for (const [k, kapanis] of uretimde)
        if (kapanis === null && k.kuralVar && k.dogus <= y) uretimde.set(k, y + DUZELTME);
    }
    for (const [k, kapanis] of uretimde)
      if (k.dogus <= y && (kapanis === null || y < kapanis)) maruziyet++;
  }
  return { ad, bloke, yanlisBloke, ulasan: uretimde.size,
    kapanmayan: [...uretimde.values()].filter((v) => v === null).length, maruziyet,
    dk: Math.round((sablonTarar ? YAPI * SABLON_DK : 0) + kosum * CALISAN_DK +
      bloke * BLOKE_DK) };
}

const YERLESIM = [
  olc("once: kesin ihlal durur", (k) => karar(k) === "ihlal", false, true),
  olc("once: belirlenemez de durur",
    (k) => karar(k) === "ihlal" || karar(k) === "belirlenemez", false, true),
  olc("yalniz dagitimdan sonra", () => false, true, false),
  olc("once kesin + sonra denetim", (k) => karar(k) === "ihlal", true, true),
];

const gen = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j])
  : String(v).padEnd(B[0]))).join("");
const y = gen([28, 8, 14, 9, 13, 12, 11]);
console.log(`${YAPI} yapi; kaynak olustuktan sonra her yapida acik kalan ihlal maruziyet ` +
  `sayilir. Dagitim sonrasi denetim ${DENETIM} yapida bir kosar, duzeltme ${DUZELTME} yapi surer`);
console.log(y(["yerlesim", "bloke", "yanlis bloke", "ulasan", "kapanmayan",
  "maruziyet", "toplam dk"]));
for (const o of YERLESIM) {
  console.log(y([o.ad, o.bloke, o.yanlisBloke, o.ulasan, o.kapanmayan, o.maruziyet, o.dk]));
}

const [kesin, genis, sadeceSonra, ikisi] = YERLESIM;
console.log(`\ndagitimdan once duran kapi ${kesin.ulasan} ihlali uretime birakiyor ve ` +
  `${kesin.bloke} yapi durduruyor; kapiyi belirlenemez kalemlere genisletmek ulasani ` +
  `${genis.ulasan}'a indiriyor ama ${genis.yanlisBloke} yapiyi bosuna durduruyor`);
console.log(`yalniz dagitimdan sonra tarayan denetim hicbir yapiyi durdurmuyor ve ` +
  `${sadeceSonra.ulasan} ihlalin ${sadeceSonra.ulasan - sadeceSonra.kapanmayan} tanesini ` +
  `kapatiyor; maruziyeti ${sadeceSonra.maruziyet}, yani kesin kapinin ` +
  `${(sadeceSonra.maruziyet / kesin.maruziyet).toFixed(2)} kati`);
console.log(`ikisini birlikte kosturmak maruziyeti ${ikisi.maruziyet} yapi-kaleme indiriyor; ` +
  `geriye kalan ${ikisi.kapanmayan} ihlal her yerlesimde ayni, cunku o alan sinifi icin ` +
  `yazilmis kural yok`);
```

```
60 yapi; kaynak olustuktan sonra her yapida acik kalan ihlal maruziyet sayilir. Dagitim sonrasi denetim 5 yapida bir kosar, duzeltme 3 yapi surer
yerlesim                       bloke  yanlis bloke   ulasan   kapanmayan   maruziyet  toplam dk
once: kesin ihlal durur           34             0       45           45        1339        800
once: belirlenemez de durur       52             9       19           19         572       1160
yalniz dagitimdan sonra            0             0      100           19         979        108
once kesin + sonra denetim        34             0       45           19         697        908

dagitimdan once duran kapi 45 ihlali uretime birakiyor ve 34 yapi durduruyor; kapiyi belirlenemez kalemlere genisletmek ulasani 19'a indiriyor ama 9 yapiyi bosuna durduruyor
yalniz dagitimdan sonra tarayan denetim hicbir yapiyi durdurmuyor ve 100 ihlalin 81 tanesini kapatiyor; maruziyeti 979, yani kesin kapinin 0.73 kati
ikisini birlikte kosturmak maruziyeti 697 yapi-kaleme indiriyor; geriye kalan 19 ihlal her yerlesimde ayni, cunku o alan sinifi icin yazilmis kural yok
```

Birinci satır dağıtımdan önce koşmanın kazancını yazıyor: 34 yapı duruyor, hiçbiri boşuna
durmuyor ve durdurulan 55 kalemde kaynak hiç oluşmuyor. Ama aynı satırın maruziyet sütunu
1 339 yapı-kalem. Kapının geçirdiği 45 ihlal üretime ulaşıyor ve hiçbiri kapanmıyor, çünkü bu
yerleşimde onları arayan başka bir denetim yok.

Üçüncü satır sıralamayı bozuyor. Yalnız dağıtımdan sonra çalışan kaynağı okuyan denetim hiçbir
yapıyı durdurmuyor, 108 model dakikası harcıyor — kesin kapılı yerleşimin yedide birinden az — ve
maruziyeti 979 yapı-kaleme indiriyor: kesin kapının **0,73 katı**. Nedeni birinci bölümde
yazılıydı. Çalışan kaynakta belirlenemez alan yoktur; değer artık hesaplanmıştır ve okunur.
Dağıtımdan önce koşan kural 26 ihlali hiç görmüyordu, dağıtımdan sonra koşan denetim hepsini
görüyor. **Erken koşmak her zaman daha çok yakalamak demek değildir; şablon metninin
söyleyemediği şeyi hiçbir erken kural söyleyemez.**

İkinci satır bu boşluğu kapının kendisiyle kapatmayı deniyor ve en düşük maruziyeti veriyor: 572.
Bedeli iki sütunda yazılı — 52 bloke yapı, 9'u boşuna, toplam 1 160 dakika. Bu, birinci bölümdeki
takasın hattaki karşılığıdır: belirsizliği ihlal saymak maruziyeti yarıya indirir ve her altıncı
durdurmayı gereksiz kılar.

Dördüncü satır ikisini birleştiriyor. Kesin ihlaller dağıtımdan önce durur — 34 yapı, sıfır
boşuna — ve kalanı beş yapıda bir koşan çalışan kaynak denetimi kapatır. Maruziyet 697,
toplam 908 dakika. Her iki denetim de aynı 19 ihlali görmez ve bu sayı dört yerleşimde de aynıdır.
**İki denetimi de koşturmak, kural seti sessiz kalan alan sınıfını sesli kılmaz.**

#### Özet

- Şablon taraması okunabilir alanda yanlış pozitif üretmez: 55 kesin bulgunun 55'i gerçektir.
  Belirsizliğin tek kaynağı değeri dağıtımda hesaplanan alandır.
- 420 kalemin 91'i belirlenemezdir ve dağılım sınıfa göre keskin ayrışır: kimlik yetkisinde 66
  kalemin 39'u, ağ erişim kuralında 85 kalemin 8'i.
- Belirlenemez kalemin iki ele alışı arasında orta yol yoktur: geçirmek 26 gerçek ihlali görünmez
  kılar, durdurmak 65 uygun kalemi boşuna bloklar ve raporun yüzde 44,5'ini yanlış pozitife çevirir.
- Kural yazılmamış 132 kalemdeki 19 ihlal hiçbir ele alışta ve hiçbir yerleşimde görünmez; bu sayı
  ayarla da yerleşimle de değişmez.
- Erken koşmak her zaman daha çok yakalamak değildir: kesin kapı 34 yapı durdurup 1 339 yapı-kalem
  maruziyet bırakır, yalnız dağıtımdan sonra tarayan denetim hiçbir yapıyı durdurmadan 979'a iner,
  çünkü çalışan kaynakta belirlenemez alan yoktur.
- İkisini birlikte koşturmak maruziyeti 697 yapı-kaleme, toplam bedeli 908 dakikaya getirir ve
  sıfır boşuna blokajla çalışır.

#### Sonraki Adım

Altı denetim, her değişiklikte neyin tarandığını ve neyin kaçtığını saydı: kod konumu, çalışan uç,
bağımlılık kaydı, depo geçmişi, imaj katmanı, şablon alanı. Hepsinin ortak bir varsayımı vardı —
taranan şey **elde duran metindi**. Oysa bu ölçümlerin kendisi tersini söylüyor. İmajın 144
bulgusunun 75'i kimsenin yazmadığı temel katmandan geliyordu, bağımlılık envanterinin 210
bileşeninin 186'sı bildirim dosyasında adı bile geçmeyen geçişli bileşenlerdi. Üretime giden
yapının büyük bölümü başka yerde üretilmiştir ve o parçalar için sorulacak soru "içinde ne var"
değildir; **onu kimin, neyden, hangi ortamda ürettiğidir**. Sonraki konu bu soruyu ölçülebilir
hale getirir: bir yapı çıktısının taşıdığı bileşen listesi hangi soruları cevaplayabiliyor, bir
imza neyi kanıtlıyor ve neyi kanıtlamıyor, köken belgesindeki hangi iddia gerçekten
doğrulanabiliyor.

## Tedarik Zinciri Güvenliği

Kodun dışında kalan katmanın kanıtla ölçülmesi: bileşen envanterinin hangi soruyu cevaplayabildiği ve hangi sorunun envanterin alanları dışında kaldığı, imzanın kanıtladığı ile kanıtlamadığının ayrılması ve anahtar ele geçtiğinde şüpheli hale gelen çıktı sayısı, köken belgesinin alanlarından kaçının bağımsız doğrulanabildiği ve kaçının yalnız beyan olduğu, derleme ortamının yalıtımı ve yetki daraltmanın kalem kalem sayılması, bağımlılık politikasının kural olarak koşturulup kaç bileşeni işaretlediği ve kaç işaretin yanlış olduğu, bulguların ciddiyet, sömürülebilirlik ve maruziyetle üç kez sıralanması ve sıralamanın kaç yerde değiştiği.

### Yazılım Malzeme Listesi

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/yazilim-malzeme-listesi

Bileşen envanteri bir soru kümesiyle ölçülür: on iki sorunun kaçı envanterin alanlarıyla cevaplanıyor, kaçı hiç cevaplanmıyor, cevaplananların kaçı eksik alanla veriliyor ve envanter ne sıklıkta üretilirse sorgu anında kaç kayıtta yanılıyor.

Önceki konu her değişiklikte ne tarandığını ve neyin kaçtığını saydı. Bütün o denetimlerin ortak
bir sınırı vardı: hepsi ekibin yazdığı koda bakıyordu. Üretime giden şeyin çoğu ise başka yerde
üretilmiş bileşenlerden oluşur ve o bileşenlerin hiçbiri bir gözden geçirmeden, bir kural
setinden ya da bir desen taramasından geçmemiştir.

Bu ders o katmanı bir envantere çevirir. Kurs boyunca ölçü **kanıt**tır ve bu derste kanıtın
birimi sorudur: envanterin cevaplayabildiği soru sayısı. Sürekli Tümleştirme ve Teslimat
kursunda envanter bir hat çıktısı olarak ölçülmüştü — kapsamı ve üretim maliyeti sayılmıştı;
o ölçüm tekrarlanmaz. Buradaki nesne envanterin **yapısı**dır: hangi alanı taşıyor, o alanlarla
hangi soru cevaplanabiliyor, hangisi cevaplanamıyor.

**TZ1.** Kurgu bölgesel ölçüm ağı üç depodur — abone portalı, sayaç alım servisi ve ikisinin
paylaştığı ortak kitaplık; bağımlılık ağacı `node` ile üretilir ve bir modeldir. **TZ2.**
Envanter beş alan taşır: bileşen adı, sürüm, hangi depoya girdiği, lisans ve bağımlılık ilişkisi
(bu bileşeni hangi bileşenin getirdiği). Envanter biçiminin adı geçmez; ölçülen şey bu beş alanın
kendisidir. **TZ3.** Çözümleyici lisansı kayıtların yaklaşık altıda birinde, getiren kenarını
sekizde birinde kaydetmez; alan vardır, değer yoktur. Üretecin tohumu `20260731` görünürdür.

#### Envanteri Üretmek

```js
// olcum-agi/envanter.mjs — uc deponun bilesen envanteri uretilir (model)
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
mkdirSync(KOK, { recursive: true });
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(20260731);                                  // tohum gorunur
const sec = (d) => d[Math.floor(r() * d.length)];

// Kurgu bolgesel olcum agi: abone portali ve sayac alim servisi ortak kitapligi paylasir.
const DEPO = [["abone-portali", 11], ["sayac-alim", 9], ["ortak-kitaplik", 6]];
const HAVUZ = Array.from({ length: 70 }, (_, i) => `bilesen-${String(i + 1).padStart(2, "0")}`);
const SURUM = ["1.2", "1.9", "2.0", "2.4", "3.0", "3.6", "4.1"];
const LISANS = ["izin-veren", "izin-veren", "izin-veren", "zayif-karsilikli", "karsilikli"];

const kayit = [];
for (const [depo, dogrudanSayi] of DEPO) {
  const alinan = new Set();
  const yeni = () => { let a; do { a = sec(HAVUZ); } while (alinan.has(a)); alinan.add(a); return a; };
  const ekle = (ad, getiren) => {
    // Cozumleyici lisansi %18, getiren kenarini %12 oraninda kaydetmiyor: alan var, deger yok.
    kayit.push({ ad, surum: sec(SURUM), depo,
      lisans: r() < 0.18 ? "" : sec(LISANS),
      getiren: getiren === null ? depo : (r() < 0.12 ? "" : getiren) });
  };
  for (let i = 0; i < dogrudanSayi; i++) {
    const kok = yeni();
    ekle(kok, null);
    for (let k = 0, n = Math.floor(r() * 4); k < n; k++) {   // gecisli cocuklar
      const cocuk = yeni();
      ekle(cocuk, kok);
      if (r() < 0.35) ekle(yeni(), cocuk);                   // ikinci duzey
    }
  }
}
writeFileSync(join(KOK, "envanter.json"), JSON.stringify(kayit));

const ALAN = ["ad", "surum", "depo", "lisans", "getiren"];
const dolu = (a) => kayit.filter((k) => k[a] !== "").length;
const dogrudan = kayit.filter((k) => DEPO.some(([d]) => d === k.getiren)).length;
console.log(`tohum 20260731; depo ${DEPO.length}, havuz ${HAVUZ.length} bilesen`);
console.log(`envanter kaydi ${kayit.length} -> dogrudan ${dogrudan}, ` +
  `gecisli ${kayit.length - dogrudan}; benzersiz bilesen ${new Set(kayit.map((k) => k.ad)).size}`);
console.log(`envanter alani ${ALAN.length}: ${ALAN.join(", ")}`);
for (const a of ALAN)
  console.log(`  ${a.padEnd(8)} dolu ${String(dolu(a)).padStart(3)}/${kayit.length}` +
    ` (%${(100 * dolu(a) / kayit.length).toFixed(0)})`);
```

```
tohum 20260731; depo 3, havuz 70 bilesen
envanter kaydi 73 -> dogrudan 26, gecisli 47; benzersiz bilesen 48
envanter alani 5: ad, surum, depo, lisans, getiren
  ad       dolu  73/73 (%100)
  surum    dolu  73/73 (%100)
  depo     dolu  73/73 (%100)
  lisans   dolu  64/73 (%88)
  getiren  dolu  67/73 (%92)
```

Üç depo **26 doğrudan bağımlılık** bildirir; envanter **73 kayıt** üretir. Aradaki 47 kayıt hiçbir
ekibin seçmediği geçişli bileşenlerdir. Benzersiz bileşen sayısı 48'dir: aynı bileşen birden çok
depoda geçer, her geçişi ayrı bir kayıttır.

Alanların doluluk oranı envanterin asıl ölçüsüdür. İlk üç alan her kayıtta doludur, çünkü
çözümleyici onları bilmeden kaydı hiç üretemez. Son iki alan böyle değil: **lisans 9 kayıtta,
getiren 6 kayıtta boştur.** Boş bir alan yokluk anlamına gelmez — bileşenin lisansı vardır,
envanterde yazmıyordur. Bu ayrım bir sonraki bölümün tamamını belirler.

#### On İki Soru

Envanterin değeri üretildiği anda değil, sorulduğu anda ölçülür. Ölçünün birimi de sorudur:
bir soru envanterin alanlarıyla cevaplanabiliyorsa envanter o soruyu kapsar, cevaplanamıyorsa
soru envanterin dışındadır.

**TZ4.** Soru kümesi kurgudur ve on iki sorudan oluşur; her soru gereksindiği alanları bildirir.
Bir soru, gereksindiği bütün alanlar envanterde varsa cevaplanır; alanların herhangi biri boş
kayıt taşıyorsa cevap **eksik** sayılır.

```js
// olcum-agi/sorular.mjs — envantere on iki soru sorulur: kaci cevaplaniyor
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const kayit = JSON.parse(readFileSync(join(KOK, "envanter.json")));
const ALAN = new Set(["ad", "surum", "depo", "lisans", "getiren"]);
const DEPO = ["abone-portali", "sayac-alim", "ortak-kitaplik"];
const H = "bilesen-19";                                  // orneklerde sorulan bilesen
const adlar = [...new Set(kayit.map((k) => k.ad))];
const farkli = (ad, alan) => new Set(kayit.filter((k) => k.ad === ad).map((k) => k[alan]));

const SORU = [
  ["bu bilesen bizde var mi", ["ad"], () => (adlar.includes(H) ? "evet" : "hayir")],
  ["hangi surumde", ["ad", "surum"], () => [...farkli(H, "surum")].sort().join(", ")],
  ["hangi urune girdi", ["ad", "depo"], () => farkli(H, "depo").size + " depo"],
  ["kim getirdi", ["ad", "getiren"], () => [...farkli(H, "getiren")].join(", ")],
  ["kisitli lisansli bilesen var mi", ["lisans"],
    () => kayit.filter((k) => k.lisans === "karsilikli").length + " kayit"],
  ["kac bilesen iki surumle kosuyor", ["ad", "surum"],
    () => adlar.filter((a) => farkli(a, "surum").size > 1).length + " bilesen"],
  ["kac bilesen iki depoda ortak", ["ad", "depo"],
    () => adlar.filter((a) => farkli(a, "depo").size > 1).length + " bilesen"],
  ["dogrudan mi gecisli mi", ["getiren"],
    () => kayit.filter((k) => DEPO.includes(k.getiren)).length + " dogrudan"],
  ["bu bilesen kosum aninda cagriliyor mu", ["cagrilir"], null],
  ["bu surum envantere ne zaman girdi", ["giris"], null],
  ["son yayini ne kadar eski, kac bakimcisi var", ["bakim"], null],
  ["bu envanter hangi yapi ciktisini tarif ediyor", ["yapi"], null],
];

const bosSayisi = (alan) => kayit.filter((k) => k[alan] === "").length;
const S = [46, 13, 12, 24, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["soru", "gereken alan", "envanterde", "yanit", "tam mi"]);
let cevaplanan = 0, tamCevap = 0;
for (const [ad, gereken, calistir] of SORU) {
  const alanVar = gereken.every((a) => ALAN.has(a));
  const bos = alanVar ? Math.max(...gereken.map(bosSayisi)) : 0;
  if (alanVar) { cevaplanan++; if (bos === 0) tamCevap++; }
  satir([ad, gereken.join("+"), alanVar ? "var" : "YOK",
    alanVar ? calistir() : "-", alanVar ? (bos ? `${bos} kayit bos` : "tam") : "-"]);
}
console.log("");
console.log(`on iki sorunun ${cevaplanan}'i envanterin alanlariyla cevaplaniyor, ` +
  `${SORU.length - cevaplanan}'u alan olmadigi icin hic cevaplanmiyor`);
console.log(`cevaplanan ${cevaplanan} sorunun ${tamCevap}'i tam, ${cevaplanan - tamCevap}'i ` +
  `eksik alanla (lisans ${bosSayisi("lisans")} kayitta bos, getiren ${bosSayisi("getiren")} kayitta bos)`);
```

```
soru                                          gereken alan envanterde  yanit                   tam mi
bu bilesen bizde var mi                       ad           var         evet                    tam
hangi surumde                                 ad+surum     var         1.2, 3.6                tam
hangi urune girdi                             ad+depo      var         2 depo                  tam
kim getirdi                                   ad+getiren   var         bilesen-01, bilesen-23  6 kayit bos
kisitli lisansli bilesen var mi               lisans       var         10 kayit                9 kayit bos
kac bilesen iki surumle kosuyor               ad+surum     var         17 bilesen              tam
kac bilesen iki depoda ortak                  ad+depo      var         20 bilesen              tam
dogrudan mi gecisli mi                        getiren      var         26 dogrudan             6 kayit bos
bu bilesen kosum aninda cagriliyor mu         cagrilir     YOK         -                       -
bu surum envantere ne zaman girdi             giris        YOK         -                       -
son yayini ne kadar eski, kac bakimcisi var   bakim        YOK         -                       -
bu envanter hangi yapi ciktisini tarif ediyor yapi         YOK         -                       -

on iki sorunun 8'i envanterin alanlariyla cevaplaniyor, 4'u alan olmadigi icin hic cevaplanmiyor
cevaplanan 8 sorunun 5'i tam, 3'i eksik alanla (lisans 9 kayitta bos, getiren 6 kayitta bos)
```

İlk dört soru envanteri hakkında konuşulurken en sık sayılan dörttür ve dördü de cevaplanır:
bileşen bizde var, iki sürümü koşuyor, iki depoya girmiş, iki ayrı bileşen getirmiş. Beş alan
sekiz soruyu kapsar; envanterin kapsama oranı **8/12**'dir.

Cevaplanmayan dördü aynı sınıftandır: hiçbiri envanterin **yanlış** cevapladığı soru değildir,
dördü de envanterin sorulamayacağı sorudur. Bileşenin koşum anında çağrılıp çağrılmadığı bir
çağrı çözümlemesi ister, sürümün envantere ne zaman girdiği bir tarih alanı, paketin bakım
durumu bir dış kayıt, envanterin hangi yapı çıktısını tarif ettiği ise bir bağ. Bunlar eklenebilir
alanlardır ve eklenmedikçe soru sorulamaz. **Bir envanterin sınırı, taşıdığı alan kümesidir.**

Asıl tehlikeli sütun sonuncusudur. Cevaplanan sekiz sorunun **üçü eksik alanla cevaplanır** ve
eksiklik cevabın biçiminde görünmez. "Kısıtlı lisanslı bileşen var mı" sorusunun yanıtı **10
kayıt**tır; ama lisans alanı 9 kayıtta boştur, yani gerçek sayı 10 ile 19 arasındadır. Envanter
"10" der, bir aralık değil. Aynı biçimde "kim getirdi" sorusu iki ad döndürür, oysa altı kayıtta
getiren kenarı hiç yazılmamıştır — o altı bileşen envanterde durur ve zincirleri kopuktur.
**Boş alan bir eksiklik olarak değil, küçük bir sayı olarak görünür.**

#### Envanter Ne Zaman Üretildi

Dördüncü cevaplanmayan soru bir sonraki ölçüyü açar: envanter hangi yapı çıktısını tarif ediyor?
Bağ yoksa envanterin doğruluğu tarife değil, tazeliğe kalır.

**TZ5.** Yirmi yapılık dizi kurgudur; her yapı iki ile beş arasında kaydı değiştirir, dörtte biri
bileşeni düşürür, kalanı sürüm yükseltir. Üretim başına 41 saniyelik hat süresi **veri olarak**
verilmiştir, ölçülmüş değildir; tohum `551903` görünürdür.

```js
// olcum-agi/tazelik.mjs — envanter ne siklikta uretilirse sorgu ani kac kayitta yaniliyor
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const baslangic = JSON.parse(readFileSync(join(KOK, "envanter.json")));
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(551903);                                // tohum gorunur
const SURUM = ["1.2", "1.9", "2.0", "2.4", "3.0", "3.6", "4.1"];
const anahtar = (k) => `${k.depo}|${k.ad}`;

// Kurgu yapi dizisi: yirmi yapi. Her yapi birkac kaydi degistirir; degisim de kurgudur.
const YAPI = 20, URETIM_SANIYE = 41;                     // sure veridir, olculmus degil
const durum = new Map(baslangic.map((k) => [anahtar(k), { ...k }]));
const gecmis = [];
for (let y = 1; y <= YAPI; y++) {
  const kayitlar = [...durum.values()];
  for (let i = 0, n = 2 + Math.floor(r() * 4); i < n; i++) {
    const k = kayitlar[Math.floor(r() * kayitlar.length)];
    if (r() < 0.25) durum.delete(anahtar(k));            // bilesen dusuruldu
    else k.surum = SURUM[Math.floor(r() * SURUM.length)];
  }
  gecmis.push(new Map([...durum].map(([a, k]) => [a, { ...k }])));
}

// Uc uretim sikligi. Sorgu her yapidan sonra gelir; envanter en son uretilen surumdur.
const SIKLIK = [["her yapida", 1], ["bes yapida bir", 5], ["yirmi yapida bir", 20]];
const S = [20, 12, 16, 18, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["uretim sikligi", "uretim", "hat suresi", "yanlis kayit", "en kotu an"]);
for (const [ad, adim] of SIKLIK) {
  let toplamYanlis = 0, enKotu = 0, uretim = 0;
  for (let y = 0; y < YAPI; y++) {
    if (y % adim === 0) uretim++;
    const envanter = gecmis[Math.floor(y / adim) * adim];  // elde duran envanter
    const simdi = gecmis[y];
    let yanlis = 0;
    for (const [a, k] of simdi) if (!envanter.has(a) || envanter.get(a).surum !== k.surum) yanlis++;
    for (const a of envanter.keys()) if (!simdi.has(a)) yanlis++;   // artik olmayan kayit
    toplamYanlis += yanlis;
    enKotu = Math.max(enKotu, yanlis);
  }
  satir([ad, uretim, `${uretim * URETIM_SANIYE} s`,
    `ortalama ${(toplamYanlis / YAPI).toFixed(1)}`, `${enKotu} kayit`]);
}
console.log("");
console.log(`yirmi yapida kayit ${baslangic.length} -> ${gecmis[YAPI - 1].size}; ` +
  `uretim basina ${URETIM_SANIYE} s veri olarak verildi, tohum 551903`);
```

```
uretim sikligi      uretim      hat suresi      yanlis kayit      en kotu an
her yapida          20          820 s           ortalama 0.0      0 kayit
bes yapida bir      4           164 s           ortalama 6.2      17 kayit
yirmi yapida bir    1           41 s            ortalama 22.1     41 kayit

yirmi yapida kayit 73 -> 56; uretim basina 41 s veri olarak verildi, tohum 551903
```

Her yapıda üretilen envanter hiçbir sorguda yanılmaz ve hattan **820 saniye** alır. Beş yapıda
bir üretilen envanter 164 saniye alır ve ortalama **6,2 kayıtta** yanılır; en kötü anda 17 kayıt
yanlıştır. Yirmi yapıda bir üretilen envanter 41 saniyeye iner ve ortalama 22,1 kayıtta yanılır —
elli altı kayıtlık bir kümede.

Üç satır aynı kapının üç ayarıdır ve hattan alınan süreyle yanlış cevap sayısı ters orantılı
değil, **basamaklıdır**: sıklık beşte bire indiğinde süre beşte bire iner, yanlış kayıt ise
sıfırdan 6,2'ye çıkar. İlk basamak en pahalı olanıdır, çünkü sıfırdan farklı her sayı envanterin
verdiği cevabı bir kestirime çevirir.

Son satır ölçünün kendi sınırını da yazar: yirmi yapıda kayıt sayısı 73'ten 56'ya iner. Eski bir
envanter yalnız eksik değildir, **fazladır** da — artık kullanılmayan on yedi bileşeni koşuyormuş
gibi gösterir. Bu iki hata birbirini götürmez; ikisi de aynı sütunda sayılır ve ikisi de aynı
sonucu verir: envanterin verdiği sayı, tarif ettiğini iddia ettiği şeyi tarif etmez.

#### Özet

- Üç depo 26 doğrudan bağımlılık bildirir, envanter 73 kayıt ve 48 benzersiz bileşen üretir; 47
  kayıt hiçbir ekibin seçmediği geçişli bileşendir.
- Envanterin beş alanından üçü her kayıtta doludur; lisans 9, getiren 6 kayıtta boştur. Boş alan
  yokluk değil, kaydedilmemiş değerdir.
- On iki sorunun 8'i envanterin alanlarıyla cevaplanır, 4'ü hiç cevaplanmaz: çağrılma, envantere
  giriş tarihi, paketin bakım durumu ve envanterin hangi yapı çıktısına ait olduğu. Bir envanterin
  sınırı, taşıdığı alan kümesidir.
- Cevaplanan 8 sorunun 3'ü eksik alanla cevaplanır ve eksiklik cevabın biçiminde görünmez:
  "10 kayıt" yanıtının gerçeği 10 ile 19 arasındadır.
- Her yapıda üretim 820 saniye alır ve hiç yanılmaz; beş yapıda bir üretim 164 saniye alır,
  ortalama 6,2 kayıtta yanılır; yirmi yapıda bir üretim 41 saniyeye iner, ortalama 22,1 kayıtta
  yanılır. Eski envanter yalnız eksik değil, düşürülen on yedi bileşen kadar da fazladır.

#### Sonraki Adım

Envanter üretildi, kapsamı sayıldı ve tazeliğinin bedeli yazıldı. Cevaplanmayan dördüncü soru
ise yerinde duruyor: bu envanter hangi yapı çıktısını tarif ediyor? Envanter ayrı bir dosyadır;
yanına başka bir envanter konabilir, bir kaydı değiştirilebilir, bambaşka bir çıktıyla
eşleştirilebilir. 73 kaydın hiçbiri, tarif ettiği baytların gerçekten o baytlar olduğunu söylemez.

Sonraki ders bu bağı gerçek bir anahtar çiftiyle kurar ve tek bir soruyu ikiye ayırır: imza neyi
kanıtlar, neyi kanıtlamaz? Doğrulaması geçen bir çıktı için kaç ayrı iddia ileri sürülebilir ve
bunların kaçı imzanın gerçekten dayandığı iddiadır — ve o anahtar bir kez ele geçtiğinde geriye
dönük olarak kaç çıktı şüpheli hale gelir?

### Yapı Çıktısı İmzalama

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/yapi-ciktisi-imzalama

İmza gerçek bir anahtar çiftiyle üretilip doğrulanır ve kanıtladığı ile kanıtlamadığı ayrılır: sekiz iddianın kaçı imzayla kanıtlanıyor, anahtar ele geçtiğinde kaç çıktı geriye dönük olarak şüpheli hale geliyor ve beyan genişlediğinde imza kapsamı ile doğrulayıcının sınayabildiği alan nasıl ayrışıyor.

Önceki ders envanteri üretti ve cevaplanmayan dört sorudan biriyle bitti: bu envanter hangi yapı
çıktısını tarif ediyor? Aynı boşluk envanterle sınırlı değildir. Hattın ürettiği yapı çıktısı bir
depoya yazılır, oradan alınır ve dağıtılır; her el değiştirmede nesne aynı kalmış gibi kabul
edilir ve **kaldığının kanıtı yoktur.**

Bu ders o kanıtı gerçek bir anahtar çiftiyle üretir. Sürekli Tümleştirme ve Teslimat kursunda
imza zinciri kurulmuş, doğrulamanın hattın hangi noktasında yapıldığı ve anahtarın kaç öznede
durduğu ölçülmüştü; o ölçüm tekrarlanmaz. Buradaki eksen **iddia**dır: doğrulaması geçen bir çıktı
için hangi cümleler söylenebilir ve bunların kaçı imzanın gerçekten dayandığı cümledir.

**TZ6.** İmzalama gerçek anahtar çiftiyle yapılır, ama anahtar her koşumda yeniden üretilir;
basılan şey doğrulama sonucudur, imzanın kendisi değil. Gerçek anahtar ya da sır yazılmaz.
**TZ7.** Doğrulayıcının elindeki nesne kümesi üç kalemdir: beyan, imza ve yapı çıktısının
baytları. Kaynak ağacı, yapı tanımı ve envanter elinde yoktur.

#### Sekiz İddia

```js
// olcum-agi/iddia.mjs — imzanin kanitladigi ile kanitlamadigi: sekiz iddia sinanir
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest();
const kisa = (b) => ozet(b).toString("hex").slice(0, 12);
// Anahtar ciftleri her kosumda yeniden uretilir; gercek anahtar ya da sir yazilmaz.
const YETKILI = generateKeyPairSync("ed25519");
const BASKA = generateKeyPairSync("ed25519");    // taninan ama bu servis icin yetkisiz ozne
const GUVENILEN = [YETKILI.publicKey, BASKA.publicKey];   // dogrulayicinin tanidigi anahtarlar

const CIKTI = Buffer.concat([Buffer.from("abone-portali govde\n"), Buffer.alloc(32768, "u")]);
const beyanYaz = (c) => Buffer.from(["servis=abone-portali", `cikti=${kisa(c)}`].join("\n"));
const BEYAN = beyanYaz(CIKTI);
const IMZA = sign(null, ozet(BEYAN), YETKILI.privateKey);

// Dogrulayicinin elindeki nesne kumesi: beyan, imza, yapi ciktisi. Baska nesne yok.
const dogrula = (beyan, imza, cikti) =>
  GUVENILEN.some((a) => verify(null, ozet(beyan), a, imza)) &&
  beyan.includes(`cikti=${kisa(cikti)}`);

// Her iddia icin bir kurgu senaryo: dogrulama gecerken iddia dogru mu?
const IDDIA = [
  ["beyan taninan bir anahtarla imzalandi", () => [dogrula(BEYAN, IMZA, CIKTI), true]],
  ["beyanin baytlari imzadan sonra degismedi",
    () => [dogrula(Buffer.from(BEYAN.toString().replace("abone", "sayac")), IMZA, CIKTI), false]],
  ["tarif edilen ciktinin baytlari degismedi", () => {
    const bozuk = Buffer.from(CIKTI); bozuk[12000] = 0x41;      // tek bayt
    return [dogrula(BEYAN, IMZA, bozuk), false];
  }],
  ["imzalayan bu servis icin yetkiliydi", () =>
    [dogrula(BEYAN, sign(null, ozet(BEYAN), BASKA.privateKey), CIKTI), false]],
  ["cikti su kaynak islemesinden uretildi", () => [dogrula(BEYAN, IMZA, CIKTI), false]],
  ["imza su tarihte uretildi", () => [dogrula(BEYAN, IMZA, CIKTI), false]],
  ["cikti hat denetimlerinden gecti", () => [dogrula(BEYAN, IMZA, CIKTI), false]],
  ["icindeki bilesenler envanterdekilerdir", () => [dogrula(BEYAN, IMZA, CIKTI), false]],
];

const S = [42, 12, 13, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["iddia", "dogrulama", "iddia dogru", "imza kanitliyor mu"]);
let kanit = 0;
for (const [ad, calistir] of IDDIA) {
  const [gecti, dogruMu] = calistir();
  const kanitliyor = gecti === dogruMu;            // dogrulama iddiayla ayni yone gidiyor mu
  if (kanitliyor) kanit++;
  satir([ad, gecti ? "geciyor" : "dusuyor", dogruMu ? "evet" : "hayir",
    kanitliyor ? "evet" : "HAYIR"]);
}
console.log("");
console.log(`beyan ${BEYAN.length} bayt, imza ${IMZA.length} bayt, cikti ${CIKTI.length} bayt`);
console.log(`sekiz iddianin ${kanit}'i imzayla kanitlaniyor, ${IDDIA.length - kanit}'i ` +
  `kanitlanmiyor: dogrulama geciyor, iddia yanlis`);
```

```
iddia                                     dogrulama   iddia dogru  imza kanitliyor mu
beyan taninan bir anahtarla imzalandi     geciyor     evet         evet
beyanin baytlari imzadan sonra degismedi  dusuyor     hayir        evet
tarif edilen ciktinin baytlari degismedi  dusuyor     hayir        evet
imzalayan bu servis icin yetkiliydi       geciyor     hayir        HAYIR
cikti su kaynak islemesinden uretildi     geciyor     hayir        HAYIR
imza su tarihte uretildi                  geciyor     hayir        HAYIR
cikti hat denetimlerinden gecti           geciyor     hayir        HAYIR
icindeki bilesenler envanterdekilerdir    geciyor     hayir        HAYIR

beyan 39 bayt, imza 64 bayt, cikti 32788 bayt
sekiz iddianin 3'i imzayla kanitlaniyor, 5'i kanitlanmiyor: dogrulama geciyor, iddia yanlis
```

İlk üç satır imzanın gerçekten yaptığı işi verir. Doğrulama geçtiğinde şu üç cümle kesindir:
beyan tanınan bir anahtarla imzalanmıştır, imzadan sonra beyanın baytları değişmemiştir ve
beyanın özetle bağladığı çıktının baytları değişmemiştir. İlk ikisi 39 baytlık beyanla, üçüncüsü
32.788 baytlık çıktının yeniden okunmasıyla sınanır. **İmzanın kanıtladığı üç cümle bunlardır.**

Kalan beş satırın hepsinde doğrulama geçer ve iddia yanlıştır. Dördüncü satırda beyan geçerli bir
anahtarla imzalanmıştır, ama imzalayan bu servisin yetkili öznesi değildir; doğrulayıcı "tanıdığım
bir anahtar" der ve geçirir — yetki, imzanın değil, anahtar listesinin taşıdığı bir karardır.
Beşinci, altıncı, yedinci ve sekizinci satırlarda beyan o cümleleri **hiç kurmaz**: kaynak
işlemesinden, imzanın üretildiği andan, hat denetimlerinden ve çıktının içindeki bileşenlerden
39 baytlık beyanda tek kelime geçmez. Doğrulama geçer, çünkü doğrulamanın bakacağı bir alan yoktur.

Bu ayrım tek cümleye iner: **imza "bu çıktı şu anahtarla imzalandı" der, "bu çıktı güvenli" demez.**
İkinci cümlenin doğru olması imzayla ilgili değildir; imzanın altındaki beyanda ne yazdığıyla,
o yazının kim tarafından doldurulduğuyla ve doğrulayıcının o alanı sınayıp sınayamadığıyla ilgilidir.

Üç kanıtlanan cümlenin bedeli de eşit değildir. İlk ikisi yalnız beyanla sınanır: 39 baytlık beyan
ve 64 baytlık imza, toplam 103 bayt okuma. Üçüncüsü çıktının baytlarını yeniden okumayı gerektirir
— 32.788 bayt, yani **318 katı**. Bu oran doğrulamanın nerede yapılabileceğini belirler: nesnesi
elinde olmayan bir doğrulayıcı üçüncü cümleyi hiç kuramaz ve elinde yalnız beyan olan bir kapı,
imzayı doğruladığı hâlde tek baytı değişmiş bir çıktıyı geçirir.

#### Anahtar Ele Geçtiğinde

İmzanın kanıtladığı üç cümle bile bir koşula bağlıdır: gizli anahtarı yalnız yetkili öznenin
kullanabildiği. O koşul kalktığında sorulacak sayı şudur — kaç çıktı şüpheli hale gelir?

**TZ8.** Yüz yirmi kurgu hat koşumu birer çıktı üretip imzalar; anahtarı ele geçiren özne dört
çıktıyı daha imzalar ve bu dördü hiçbir hat koşumu değildir. **TZ9.** Yalnız ekleme yapılan yayım
kaydı her girdiyi bir önceki zincir özetiyle birlikte özetler; geçmişe girdi sokmak zinciri kırar.

```js
// olcum-agi/anahtar-kaybi.mjs — anahtar ele gectiginde kac cikti supheli hale geliyor
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest();
const kisa = (b) => ozet(b).toString("hex").slice(0, 12);
const ANAHTAR = generateKeyPairSync("ed25519");   // her kosumda yeniden uretilir

// Kurgu: yuz yirmi hat kosumu, her biri bir cikti uretip imzaliyor. Kosum kimligi hatta kayitli.
const KOSUM = 120, SAHTE = 4;
const kayitYap = (no, kimlik) => {
  const cikti = Buffer.alloc(4096, String.fromCharCode(97 + (no % 26)));
  const beyan = Buffer.from(`servis=abone-portali\nkosum=${kimlik}\ncikti=${kisa(cikti)}`);
  return { no, kimlik, beyan, imza: sign(null, ozet(beyan), ANAHTAR.privateKey) };
};
const gercek = Array.from({ length: KOSUM }, (_, i) => kayitYap(i + 1, `k-${i + 1}`));

// Anahtari ele geciren ozne dort cikti imzalar; bunlarin hicbiri bir hat kosumu degildir.
const sahte = Array.from({ length: SAHTE }, (_, i) => kayitYap(1000 + i, `x-${i + 1}`));

// Yalniz ekleme yapilan yayim kaydi: her girdi bir onceki zincir ozetini de ozetler.
const zincirle = (kayitlar) => kayitlar.reduce((z, k) =>
  [...z, ozet(Buffer.concat([z.at(-1) ?? Buffer.alloc(32), k.beyan, k.imza]))], []);
const ZINCIR = zincirle(gercek);

// Uc kayit turu. Sahte kayitlar duz listede araya sokulur, zincirde de ayni yere sokulur.
const araya = [...gercek.slice(0, 60), ...sahte, ...gercek.slice(60)];
const kirikIndis = zincirle(araya).findIndex((z, i) => i < ZINCIR.length && !z.equals(ZINCIR[i]));
const gecen = [...gercek, ...sahte]
  .filter((k) => verify(null, ozet(k.beyan), ANAHTAR.publicKey, k.imza)).length;

const S = [34, 15, 14, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["kayit turu", "dogrulamayi", "araya sokma", "supheli cikti"]);
satir(["kayit yok, yalniz imza", gecen, "gorulmez", `${gecen}/${gecen}`]);
satir(["duz kosum listesi", gecen, "gorulmez", `${gecen}/${gecen}`]);
satir(["zincirli yayim kaydi", gecen,
  kirikIndis >= 0 ? `indis ${kirikIndis}` : "gorulmez", `${SAHTE}/${gecen}`]);

// Zincir gecmisi dondurur: gecmise sokulan girdi kirilma verir, sonuna eklenen gizlenemez.
console.log("");
console.log(`imzalanan cikti ${gercek.length} + ${sahte.length} = ${gecen}; ` +
  `hepsi ayni anahtarla, hepsi dogrulamayi geciyor`);
console.log(`zincir ${ZINCIR.length} girdi; gecmise sokulan ${SAHTE} girdi ` +
  `zinciri indis ${kirikIndis}'ta kiriyor, duz listede iz birakmiyor`);
console.log(`geriye donuk suphe: kayitsiz ${gecen} cikti, zincirli kayitla ${SAHTE} cikti`);
```

```
kayit turu                        dogrulamayi    araya sokma   supheli cikti
kayit yok, yalniz imza            124            gorulmez      124/124
duz kosum listesi                 124            gorulmez      124/124
zincirli yayim kaydi              124            indis 60      4/124

imzalanan cikti 120 + 4 = 124; hepsi ayni anahtarla, hepsi dogrulamayi geciyor
zincir 120 girdi; gecmise sokulan 4 girdi zinciri indis 60'ta kiriyor, duz listede iz birakmiyor
geriye donuk suphe: kayitsiz 124 cikti, zincirli kayitla 4 cikti
```

Üç satırın ilk sütunu aynıdır ve dersin en sert sayısı odur: **124 çıktının 124'ü doğrulamayı
geçer.** Dört sahte çıktı gerçek anahtarla imzalanmıştır; doğrulayıcının onları ayırt etmesini
sağlayacak hiçbir şey yoktur, çünkü imza yalnız "bu anahtarla imzalandı" der ve bu doğrudur.

Fark son sütunda çıkar. Kayıt tutulmayan düzende anahtarın ele geçtiği duyulduğunda hangi imzanın
ne zaman üretildiği bilinemez; **hiçbir çıktı için "bu, ele geçmeden önce imzalanmıştı" cümlesi
kurulamaz** ve 124 çıktının 124'ü şüpheli olur. Bu, kayıt tutmamanın gerçek bedelidir: kayıp
imzanın kendisinde değil, ondan sonra yapılması gereken işin büyüklüğündedir.

Düz bir koşum listesi bunu değiştirmez. Liste sıradan bir dosyadır; dört girdi araya sokulur ve
**hiçbir iz kalmaz** — liste artık dördünü de hattın koşumu gibi gösterir. Zincirli yayım kaydında
aynı sokma **indis 60'ta** zinciri kırar. Zincirin yaptığı iş imzalamayı engellemek değildir;
engelleyemez de. Yaptığı iş **geçmişi dondurmak**tır: sonradan üretilen bir imza ancak sonuna
eklenebilir, geçmişe yerleştirilemez. Şüpheli çıktı sayısı 124'ten **4'e** iner ve bu 4, kaydın
sonundaki eşleşmeyen girdilerdir.

İki sayı arasındaki fark tespitte değil, **şüphenin kapsamında**dır. Her iki düzende de dört sahte
çıktı üretilmiştir ve her ikisinde de doğrulama onları geçirir. Değişen şey, olay anlaşıldıktan
sonra yapılacak iştir: kayıtsız düzende 124 çıktının hepsi yeniden üretilip yeniden imzalanmalıdır,
zincirli kayıtta yalnız 4'ü. Bu, imzalamanın hattan aldığı sürenin karşılığının nerede ödendiğini
gösterir — imza atmanın maliyeti koşum başına birkaç saniyedir, kayıt tutmamanın maliyeti tek bir
olayda 120 çıktılık bir yeniden üretimdir.

#### Beyan Genişlediğinde

Beş kanıtlanmayan iddianın dördü beyanda o alanların hiç bulunmamasından geliyordu. Alanları
eklemek yeter mi?

**TZ10.** Beyan dokuz alana çıkarılır ve dört alana kasten yanlış değer yazılır; beyan gerçekten
imzalanır. Doğrulayıcının elindeki tek nesne yine yapı çıktısıdır.

```js
// olcum-agi/beyan-genisletme.mjs — beyan genisleyince imza kapsami ile sinanabilirlik ayrisir
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest();
const kisa = (b) => ozet(b).toString("hex").slice(0, 12);
const A = generateKeyPairSync("ed25519");
const CIKTI = Buffer.concat([Buffer.from("abone-portali govde\n"), Buffer.alloc(32768, "u")]);

// Dogrulayicinin elinde yalnizca yapi ciktisi var; kaynak agaci, yapi tanimi ve envanter yok.
const ELDEKI = { cikti: CIKTI };
// Dokuz alan. `dogru` alanin gercek degeri, `yazilan` beyana konan deger.
const ALAN = [
  ["servis", "abone-portali", "abone-portali"],
  ["cikti", kisa(CIKTI), kisa(Buffer.alloc(8, "z"))],        // kasten yanlis
  ["kaynak", "9f31c0aa4b12", "0000deadbeef"],                // kasten yanlis
  ["yapi-tanimi", "51ba7c0e93d4", "51ba7c0e93d4"],
  ["envanter", "c4e0a71f2b88", "c4e0a71f2b88"],
  ["temel-imaj", "7ad3e1c95006", "111122223333"],            // kasten yanlis
  ["kosum", "k-118", "k-118"],
  ["zaman", "yapi-basi", "yapi-basi"],
  ["denetim", "iki bulgu acik", "bulgu yok"],                // kasten yanlis
];

const beyan = Buffer.from(ALAN.map(([a, , y]) => `${a}=${y}`).join("\n"));
const imza = sign(null, ozet(beyan), A.privateKey);
const imzaGecti = verify(null, ozet(beyan), A.publicKey, imza);

const S = [14, 18, 24, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["alan", "beyandaki deger", "yeniden hesaplanabilir", "yakalandi mi"]);
let sinanabilir = 0, yanlis = 0, yakalanan = 0;
for (const [ad, dogru, yazilan] of ALAN) {
  // Bir alan ancak dogrulayicinin elindeki bir nesneden yeniden hesaplanabiliyorsa sinanir.
  const hesaplanir = ad in ELDEKI;
  const dogruMu = dogru === yazilan;
  if (hesaplanir) sinanabilir++;
  if (!dogruMu) yanlis++;
  const yakala = hesaplanir && !dogruMu && kisa(ELDEKI[ad]) !== yazilan;
  if (yakala) yakalanan++;
  satir([ad, dogruMu ? "dogru" : "YANLIS", hesaplanir ? "evet" : "hayir",
    yakala ? "evet" : (dogruMu ? "-" : "HAYIR")]);
}
console.log("");
console.log(`beyan ${ALAN.length} alan, ${beyan.length} bayt; imza dogrulamasi ` +
  `${imzaGecti ? "geciyor" : "dusuyor"}`);
console.log(`imzanin kapsadigi alan ${ALAN.length}/${ALAN.length}, ` +
  `dogrulayicinin sinayabildigi alan ${sinanabilir}/${ALAN.length}`);
console.log(`kasten yanlis yazilan ${yanlis} alanin ${yakalanan}'i yakalandi, ` +
  `${yanlis - yakalanan}'u beyanla birlikte imzalandi ve gecti`);
```

```
alan          beyandaki deger   yeniden hesaplanabilir  yakalandi mi
servis        dogru             hayir                   -
cikti         YANLIS            evet                    evet
kaynak        YANLIS            hayir                   HAYIR
yapi-tanimi   dogru             hayir                   -
envanter      dogru             hayir                   -
temel-imaj    YANLIS            hayir                   HAYIR
kosum         dogru             hayir                   -
zaman         dogru             hayir                   -
denetim       YANLIS            hayir                   HAYIR

beyan 9 alan, 176 bayt; imza dogrulamasi geciyor
imzanin kapsadigi alan 9/9, dogrulayicinin sinayabildigi alan 1/9
kasten yanlis yazilan 4 alanin 1'i yakalandi, 3'u beyanla birlikte imzalandi ve gecti
```

Beyan iki alandan dokuza çıktı ve imzanın kapsadığı alan **9/9** oldu: dokuzunun hiçbiri imzadan
sonra değiştirilemez. Doğrulayıcının sınayabildiği alan ise **1/9**'da kaldı. Aradaki fark dersin
son ayrımıdır: **imza kapsamı ile doğrulanabilirlik ayrı iki niceliktir.** İlki beyanı yazan
tarafın kararıdır, ikincisi doğrulayanın elindeki nesne kümesinin.

Son satır bunun sonucudur. Dört alan kasten yanlış yazılmıştır; yalnız biri — çıktı özeti —
yakalanır, çünkü doğrulayıcı çıktının baytlarını okuyup yeniden hesaplayabilir. Diğer üçü
yakalanmaz ve daha kötüsü, **imzalanmış olarak geçer**. İmzalı bir alan, imzasız bir alandan daha
güvenilir görünür; oysa imza yalnız "bu değer sonradan değiştirilmedi" der, "bu değer doğru
yazıldı" demez.

#### Özet

- Doğrulaması geçen bir çıktı için sekiz iddiadan 3'ü imzayla kanıtlanır: beyan tanınan bir
  anahtarla imzalandı, beyanın baytları değişmedi, özetle bağlanan çıktının baytları değişmedi.
- Kalan 5 iddiada doğrulama geçer ve iddia yanlıştır: yetki anahtar listesinin kararıdır, kaynak
  işlemesi, imzalama anı, denetim sonucu ve bileşen listesi 39 baytlık beyanda hiç geçmez.
- Anahtar ele geçtiğinde 124 çıktının 124'ü doğrulamayı geçer; kayıt tutulmayan düzende hiçbiri
  için "ele geçmeden önce imzalanmıştı" denemez ve 124'ü şüpheli olur.
- Düz koşum listesine dört girdi iz bırakmadan sokulur; zincirli yayım kaydında aynı sokma indis
  60'ta zinciri kırar ve şüpheli çıktı 4'e iner. Zincir imzalamayı engellemez, geçmişi dondurur.
- Beyan dokuz alana çıktığında imzanın kapsadığı alan 9/9, doğrulayıcının sınayabildiği alan
  1/9'dur; kasten yanlış yazılan dört alanın üçü imzalanmış olarak geçer.

#### Sonraki Adım

İmza artık kurulu ve neyi kanıtlayıp neyi kanıtlamadığı yazılı. Son ölçüm bir soruyu ortada
bıraktı: doğrulayıcı dokuz alanın sekizini sınayamıyorsa o sekiz alanı beyana yazmanın anlamı ne?

Cevap alanların kendisinde değil, **kimin doldurduğunda ve neyle karşılaştırılabildiğinde**dir.
Bir alanın değeri, doğrulayanın bağımsız olarak elde edebildiği bir nesneden yeniden
hesaplanabiliyorsa o alan bir kanıttır; hesaplanamıyorsa bir beyandır ve beyanın doğruluğu onu
yazana güvenmeye kalır. Sonraki ders yapının nerede ve nasıl üretildiğini anlatan belgeyi alan
alan kurar ve tek bir sayıyı çıkarır: bu belgenin kaç alanı bağımsız olarak doğrulanabiliyor,
kaçı yalnız beyan olarak kalıyor?

### Köken Bilgisi

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/koken-bilgisi

Yapının nerede ve nasıl üretildiğini anlatan belge alan alan kurulur ve tek bir sayıya indirgenir: on dört alanın kaçı bağımsız olarak doğrulanabiliyor, kaçı yalnız beyan olarak kalıyor, yeniden üretim kaç alanı kanıta çeviriyor ve doğrulama eşiği yükseldikçe hattan kaç saniye gidiyor.

Önceki ders imzanın kapsadığı alan ile doğrulayıcının sınayabildiği alanı ayırdı: beyan dokuz
alana çıktığında imza dokuzunu da kapsıyordu, ama doğrulayıcı yalnız birini yeniden
hesaplayabiliyordu. Kalan sekiz alan imzalanmış olarak geçiyordu ve imzalı olmak onları
doğru yapmıyordu.

Bu ders o sekiz alanın yerine geçen belgeyi kurar: yapının nerede ve nasıl üretildiğini anlatan
köken bilgisi. Ölçü tek bir sayıdır — belgenin kaç alanı **bağımsız olarak doğrulanabiliyor**,
kaçı yalnız beyan olarak kalıyor. Bir alan, doğrulayanın kendi elde edebildiği bir nesneden
yeniden hesaplanabiliyorsa kanıttır; hesaplanamıyorsa beyandır ve doğruluğu onu yazana güvenmeye
kalır.

**TZ11.** Köken belgesi dört öbekte on dört alandır: hangi kaynak, hangi işlem, hangi ortam, hangi
girdi. Belge ve nesneler `node` ile modellenir. **TZ12.** Doğrulayıcının bağımsız elde edebildiği
nesne kümesi dörttür: kaynak ağacı, yapı tanımı dosyası, bağımlılık kilit dosyası ve yapı
çıktısının baytları.

#### Belgenin On Dört Alanı

```js
// olcum-agi/koken.mjs — koken belgesi alan alan kurulur: kaci bagimsiz dogrulanabiliyor
import { createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest("hex").slice(0, 12);

// Kurgu bagimsiz nesneler: dogrulayici bunlari kendi elde edebilir ve yeniden ozetleyebilir.
const KAYNAK = Buffer.from(["ana.js", "olcum.js", "ayar.json"]
  .map((y) => `${y}\n// kurgu abone portali\n`).join(""));
const YAPI_TANIMI = Buffer.from("adim: derle, sina, paketle, imzala\n");
const KILIT = Buffer.from(Array.from({ length: 73 }, (_, i) => `bilesen-${i}@2.0`).join("\n"));
const CIKTI = Buffer.concat([Buffer.from("abone-portali\n"), Buffer.alloc(16384, "u")]);
const ELDEKI = { kaynak: KAYNAK, "yapi-tanimi": YAPI_TANIMI, kilit: KILIT, cikti: CIKTI };

// Koken belgesi: dort obek, on dort alan. `yazilan` belgeye konan deger.
const BELGE = [
  ["kaynak", "depo", "abone-portali", false],
  ["kaynak", "isleme", ozet(KAYNAK), true],
  ["kaynak", "dal", "ana", false],
  ["islem", "yapi-tanimi", ozet(YAPI_TANIMI), true],
  ["islem", "adim-listesi", "derle, sina, paketle, imzala", false],
  ["islem", "cagri-parametresi", "hedef=uretim", false],
  ["ortam", "calistirici-turu", "yalitilmis-kap", false],
  ["ortam", "temel-imaj", "7ad3e1c95006", false],
  ["ortam", "yalitim-duzeyi", "is basina yeni calistirici", false],
  ["ortam", "ag-erisimi", "kapali", false],
  ["girdi", "kilit", ozet(KILIT), true],
  ["girdi", "ortam-degiskeni-adlari", "HEDEF, SURUM", false],
  ["girdi", "onbellek-anahtari", "b41c-2", false],
  ["cikti", "cikti", ozet(CIKTI), true],
];

// Bir alan ancak elde edilebilir bir nesneden yeniden hesaplanabiliyorsa kanittir.
const S = [8, 24, 29, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["obek", "alan", "dogrulama yolu", "tur"]);
let kanit = 0;
for (const [obek, alan, yazilan, hesaplanir] of BELGE) {
  if (hesaplanir) kanit++;
  satir([obek, alan, hesaplanir ? "nesne yeniden ozetlenir" : "karsilastirilacak nesne yok",
    hesaplanir ? "kanit" : "beyan"]);
}
console.log("");
console.log(`belge ${BELGE.length} alan: ${kanit} kanit, ${BELGE.length - kanit} beyan`);

// Bes alana kasten yanlis deger yazilir; dogrulayici elindeki nesnelerle sinar.
const YANLIS = { isleme: "0000deadbeef", "yapi-tanimi": "111122223333",
  "temel-imaj": "999988887777", "yalitim-duzeyi": "paylasilan calistirici", cikti: "aaaabbbbcccc" };
const anahtar = { isleme: "kaynak", "yapi-tanimi": "yapi-tanimi", kilit: "kilit", cikti: "cikti" };
let yakalanan = 0;
for (const [, alan, , hesaplanir] of BELGE) {
  if (!(alan in YANLIS)) continue;
  if (hesaplanir && ozet(ELDEKI[anahtar[alan]]) !== YANLIS[alan]) yakalanan++;
}
console.log(`kasten yanlis yazilan ${Object.keys(YANLIS).length} alanin ${yakalanan} tanesi ` +
  `yakalandi, ${Object.keys(YANLIS).length - yakalanan} tanesi beyan oldugu icin sinanmadi`);
```

```
obek    alan                    dogrulama yolu               tur
kaynak  depo                    karsilastirilacak nesne yok  beyan
kaynak  isleme                  nesne yeniden ozetlenir      kanit
kaynak  dal                     karsilastirilacak nesne yok  beyan
islem   yapi-tanimi             nesne yeniden ozetlenir      kanit
islem   adim-listesi            karsilastirilacak nesne yok  beyan
islem   cagri-parametresi       karsilastirilacak nesne yok  beyan
ortam   calistirici-turu        karsilastirilacak nesne yok  beyan
ortam   temel-imaj              karsilastirilacak nesne yok  beyan
ortam   yalitim-duzeyi          karsilastirilacak nesne yok  beyan
ortam   ag-erisimi              karsilastirilacak nesne yok  beyan
girdi   kilit                   nesne yeniden ozetlenir      kanit
girdi   ortam-degiskeni-adlari  karsilastirilacak nesne yok  beyan
girdi   onbellek-anahtari       karsilastirilacak nesne yok  beyan
cikti   cikti                   nesne yeniden ozetlenir      kanit

belge 14 alan: 4 kanit, 10 beyan
kasten yanlis yazilan 5 alanin 3 tanesi yakalandi, 2 tanesi beyan oldugu icin sinanmadi
```

Belge on dört alan taşır ve **dördü kanıttır**. Dördünün ortak özelliği şudur: hepsi bir nesnenin
özetidir ve o nesne doğrulayanın elinde vardır. İşleme özeti kaynak ağacından, yapı tanımı özeti
depodaki dosyadan, kilit özeti bağımlılık kilit dosyasından, çıktı özeti yapı çıktısının
baytlarından yeniden hesaplanır. Belge yanlış yazsa bile bu dört alanda yalan tutmaz.

Kalan **on alan beyandır** ve ilginç olan, aralarında hiç önemsiz bir alan bulunmamasıdır. Yalıtım
düzeyi, ağ erişimi, çalıştırıcı türü ve temel imaj — dördü de yapının güvenilirliğine dair en çok
şey söyleyen alanlardır. Hiçbiri sınanamaz, çünkü hiçbirinin karşılığı doğrulayanın elde
edebileceği bir nesne değildir. "Ağ erişimi kapalıydı" cümlesi yapıyı koşturan tarafın kendi
hakkında verdiği bir ifadedir.

Son satır bu ayrımı sayıya çevirir: beş alana kasten yanlış değer yazıldığında **üçü yakalanır,
ikisi sınanmaz**. Yakalanmayan ikisi temel imaj ve yalıtım düzeyidir. Belgede "iş başına yeni
çalıştırıcı" yazar, gerçekte paylaşılan çalıştırıcı kullanılmıştır ve hiçbir doğrulama bunu
görmez. **Bir köken belgesinin değeri alan sayısıyla değil, kanıt olan alan sayısıyla ölçülür.**

#### Yeniden Üretim Neyi Kanıta Çevirir

On beyan alanının bir bölümü aslında çıktıya girer: adım listesi, çağrı parametresi ve temel imaj
farklı olsaydı çıktı da farklı olurdu. Bu, sınanabilirlik için bir kaldıraç sunar — aynı bildirilen
girdilerle yapı yeniden koşturulur ve çıktı aynı çıkarsa bildirim doğrulanmış olur.

**TZ13.** Yapı işlevi kurgudur ve bildirilen girdilerden çıktı üretir. On iki yapının üçünde
**bildirilmemiş bir girdi** çıktıya girer, birinde çıktı sonradan değiştirilmiştir; tohum `410277`
görünürdür.

```js
// olcum-agi/yeniden-uretim.mjs — yeniden uretim hangi alani kanita cevirir
import { createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest("hex").slice(0, 12);
// Kurgu yapi islevi: bildirilen girdilerden cikti uretir. Gizli girdi varsa ciktiya o da girer.
const yap = (g) => Buffer.from([g.kaynak, g.kilit, g.adimlar, g.parametre, g.temelImaj,
  g.gizli ?? ""].join("|"));

const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(410277);                                  // tohum gorunur

// On iki kurgu yapi. Uc tanesinde bildirilmemis girdi var, birinde cikti sonradan degistirildi.
const GIZLI = [3, 7, 11], DEGISTIRILEN = 9;
const YAPI = Array.from({ length: 12 }, (_, i) => {
  const no = i + 1;
  const girdi = { kaynak: `kaynak-${no}`, kilit: `kilit-${no}`, adimlar: "derle,sina,paketle",
    parametre: "hedef=uretim", temelImaj: no % 2 ? "temel-a" : "temel-b" };
  const gercek = yap(GIZLI.includes(no) ? { ...girdi, gizli: `kosum-${Math.floor(r() * 900)}` } : girdi);
  const beyanEdilen = no === DEGISTIRILEN ? ozet(Buffer.from("degistirilmis")) : ozet(gercek);
  return { no, girdi, beyanEdilen, gizliVar: GIZLI.includes(no) };
});

const S = [7, 16, 18, 12, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["yapi", "beyan edilen", "yeniden uretilen", "eslesme", "sebep"]);
let eslesen = 0, gercekBulgu = 0, yanlisIsaret = 0;
for (const y of YAPI) {
  const tekrar = ozet(yap(y.girdi));                       // yalniz BILDIRILEN girdilerle
  const esit = tekrar === y.beyanEdilen;
  if (esit) eslesen++;
  else if (y.gizliVar) yanlisIsaret++;
  else gercekBulgu++;
  if (y.no <= 4 || !esit)
    satir([y.no, y.beyanEdilen, tekrar, esit ? "evet" : "HAYIR",
      esit ? "-" : (y.gizliVar ? "bildirilmemis girdi" : "cikti degistirilmis")]);
}
console.log("");
console.log(`on iki yapinin ${eslesen}'i yeniden uretimde eslesiyor, ` +
  `${YAPI.length - eslesen} tanesi eslesmiyor: ${gercekBulgu} gercek, ${yanlisIsaret} yanlis isaret`);

// Yeniden uretim, ciktiya giren her bildirilen girdiyi sinanabilir kilar.
const CIKTIYA_GIREN = ["adim-listesi", "cagri-parametresi", "temel-imaj"];
const ONCE = 4, ALAN = 14;
console.log(`kanit alani ${ONCE}/${ALAN} -> ${ONCE + CIKTIYA_GIREN.length}/${ALAN}; ` +
  `sinanabilir hale gelen: ${CIKTIYA_GIREN.join(", ")}`);
console.log(`beyan olarak kalan ${ALAN - ONCE - CIKTIYA_GIREN.length}: ` +
  `depo, dal, calistirici-turu, yalitim-duzeyi, ag-erisimi, ortam-degiskeni-adlari, onbellek-anahtari`);
```

```
yapi   beyan edilen    yeniden uretilen  eslesme     sebep
1      bf07d2a43010    bf07d2a43010      evet        -
2      1ddced3cf18e    1ddced3cf18e      evet        -
3      e8ef828e21d6    708285ad34af      HAYIR       bildirilmemis girdi
4      ec17c7d09dca    ec17c7d09dca      evet        -
7      02dfd006731d    7837452fb129      HAYIR       bildirilmemis girdi
9      3dfd53002487    9c98f23fe16b      HAYIR       cikti degistirilmis
11     1e1126a5aff6    1f5be23c2504      HAYIR       bildirilmemis girdi

on iki yapinin 8'i yeniden uretimde eslesiyor, 4 tanesi eslesmiyor: 1 gercek, 3 yanlis isaret
kanit alani 4/14 -> 7/14; sinanabilir hale gelen: adim-listesi, cagri-parametresi, temel-imaj
beyan olarak kalan 7: depo, dal, calistirici-turu, yalitim-duzeyi, ag-erisimi, ortam-degiskeni-adlari, onbellek-anahtari
```

Yeniden üretim kanıt alanını **4'ten 7'ye** çıkarır. Kazanılan üç alanın hepsi çıktıya giren
girdilerdir: adım listesi, çağrı parametresi ve temel imaj yanlış yazılsaydı yeniden üretilen çıktı
tutmazdı. Kalan yedi alan hiçbir koşulda sınanabilir hale gelmez, çünkü **çıktıya hiç girmezler**.
Dal adı, çalıştırıcı türü, yalıtım düzeyi ve önbellek anahtarı üretilen baytları etkilemez; bir
şeyi etkilemeyen bildirim de o şeyden geri okunamaz. Belgenin yarısı beyan olarak kalır ve bu bir
eksiklik değil, yöntemin sınırıdır.

Ortadaki iki sayı ise kaldıracın bedelidir. On iki yapının dördü eşleşmez ve dördünden **yalnız
biri gerçektir**; üçünde çıktıya bildirilmemiş bir girdi karışmıştır — koşum numarası, üretim anı,
çalıştırıcı adı gibi bir değer. Bu üçünde yapı doğrudur, belge doğrudur, değişen tek şey ilan
edilmemiş bir girdinin çıktıya sızmasıdır. **Yanlış işaret oranı %75'tir** ve bu oran taramanın
işe yaramadığı durumun tam tanımıdır: dört uyarının üçü boşuna incelenirse dördüncüsü de aynı
işaretin altında kalır.

Yanlış işaretin kaynağı da yöntemin kendisindedir. Yeniden üretim, bildirilen girdi kümesinin
**eksiksiz** olduğunu varsayar; eksikse fark çıktıda görünür ama farkın nedeni belgede yazmaz.
Eşleşmeyen bir yapının hangi sınıfa girdiğini anlamak için çıktı baytları karşılaştırılır ve sızan
değer elle bulunur — bu, uyarı başına ayrı bir inceleme demektir. Üç yanlış işaretin üçü de
düzeltilebilir kusurlardır; düzeltmenin yolu belgeye alan eklemek değil, o değeri çıktıdan
çıkarmaktır. Kaldıracın kullanılabilir olması için yapının önce ilan edilmemiş girdiden
arındırılması gerekir ve bu iş kökene değil, yapı tanımına aittir.

#### Eşik Yükseldikçe

Üç ölçüm bir politika sorusunu doğurur: dağıtımdan önce hangi eşik aranmalı?

**TZ14.** Kırk kurgu dağıtım isteği vardır; bozuk istekler de **geçerli imza taşır** — önceki
dersin sonucudur, imza iddiayı kanıtlamaz. Köken üretimi 12 saniye, yeniden üretim 214 saniye
olarak **veri** verilmiştir, ölçülmüş değildir; tohum `87344` görünürdür.

```js
// olcum-agi/koken-esigi.mjs — dogrulama esigi yukseldikce gecen, bloke olan ve hattan giden sure
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(87344);                                   // tohum gorunur

// Bozuk istek de gecerli imza tasir: onceki dersin sonucu — imza iddiayi kanitlamaz.
const ISTEK = Array.from({ length: 40 }, () => {
  const bozuk = r() < 0.12;
  return { bozuk, imzali: true, kokenVar: bozuk || r() < 0.85,
    kanitUyuyor: !bozuk, gizliGirdi: r() < 0.18 };         // gizli girdi: yeniden uretim eslesmez
});

// Sureler VERI olarak verilmistir, olculmus degil: koken uretimi 12 s, yeniden uretim 214 s.
const KOKEN_S = 12, TEKRAR_S = 214;
const ESIK = [
  ["imza yeter", (i) => i.imzali, 0],
  ["imza + koken belgesi", (i) => i.imzali && i.kokenVar, KOKEN_S],
  ["imza + dort kanit alani", (i) => i.imzali && i.kokenVar && i.kanitUyuyor, KOKEN_S],
  ["imza + yeniden uretim", (i) => i.imzali && i.kokenVar && i.kanitUyuyor && !i.gizliGirdi,
    KOKEN_S + TEKRAR_S],
];

const BOZUK = ISTEK.filter((i) => i.bozuk).length;
const S = [26, 9, 9, 14, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["esik", "gecen", "bloke", "yanlis bloke", "kacan bozuk / hat suresi"]);
for (const [ad, kural, sure] of ESIK) {
  const gecen = ISTEK.filter(kural);
  const bloke = ISTEK.filter((i) => !kural(i));
  satir([ad, gecen.length, bloke.length, bloke.filter((i) => !i.bozuk).length,
    `${gecen.filter((i) => i.bozuk).length} / ${sure * ISTEK.length} s`]);
}
console.log("");
console.log(`istek ${ISTEK.length}, gercekten bozuk ${BOZUK}, ` +
  `bildirilmemis girdi tasiyan ${ISTEK.filter((i) => i.gizliGirdi).length}, ` +
  `koken belgesi olmayan ${ISTEK.filter((i) => !i.kokenVar).length}; tohum 87344`);
```

```
esik                      gecen    bloke    yanlis bloke  kacan bozuk / hat suresi
imza yeter                40       0        0             4 / 0 s
imza + koken belgesi      35       5        5             4 / 480 s
imza + dort kanit alani   31       9        5             0 / 480 s
imza + yeniden uretim     26       14       10            0 / 9040 s

istek 40, gercekten bozuk 4, bildirilmemis girdi tasiyan 7, koken belgesi olmayan 5; tohum 87344
```

İkinci satır bu kursun en sık karşılaşılan yanılgısını sayıya çevirir. Köken belgesinin **varlığını**
aramak dört bozuk isteğin dördünü de geçirir — belge bozuk isteklerde de vardır, çünkü belgeyi
üreten yapının kendisidir. Karşılığında hattan 480 saniye gider ve **beş meşru istek yanlış yere
bloke olur**. Marjinal değeri sıfır olan bir kapı budur: bir alanı dolduran, hiçbir şeyi
karşılaştırmayan bir aşama.

Üçüncü satır aynı 480 saniyeyle dört bozuk isteğin dördünü de tutar. Aradaki fark tek bir işlemdir:
belgenin dört kanıt alanı, doğrulayanın kendi elde ettiği nesnelerle **karşılaştırılır**.
Yanlış bloke sayısı değişmez, kaçan bozuk 4'ten 0'a iner. **Köken belgesinin değeri üretilmesinde
değil, karşılaştırılmasındadır.**

Dördüncü satır kaldıracın fiyatını gösterir. Yeniden üretim hiçbir yeni bozuk isteği tutmaz — dördü
zaten üçüncü satırda tutulmuştu — ama hat süresini 480 saniyeden **9.040 saniyeye** çıkarır ve
yanlış bloke sayısını 5'ten 10'a, yani kırk isteğin dörtte birine yükseltir. Kanıt alanını 7'ye
çıkarmanın karşılığı budur ve karşılığın ödenip ödenmeyeceği kaçan bozuk sayısına bakılarak karar
verilir, alan sayısına bakılarak değil. Dört isteğinden birinin boşuna durduğu bir eşik, eninde
sonunda atlanan bir eşiktir.

#### Özet

- Köken belgesi dört öbekte on dört alandır; 4 alan kanıt, 10 alan beyandır. Kanıt olanların ortak
  özelliği, doğrulayanın elindeki bir nesnenin özeti olmalarıdır.
- Beyan kalan on alan arasında önemsiz alan yoktur: yalıtım düzeyi, ağ erişimi, çalıştırıcı türü ve
  temel imaj sınanamaz. Kasten yanlış yazılan beş alanın 3'ü yakalanır, 2'si sınanmaz.
- Yeniden üretim kanıt alanını 4'ten 7'ye çıkarır; kazanılan üç alan çıktıya giren girdilerdir.
  Kalan 7 alan hiçbir koşulda sınanamaz, çünkü üretilen baytları etkilemez.
- On iki yapının 4'ü yeniden üretimde eşleşmez ve yalnız 1'i gerçektir; yanlış işaret oranı %75'tir.
- Köken belgesinin varlığını aramak 4 bozuk isteğin 4'ünü de geçirir ve 480 saniye alır; aynı süreyle
  dört kanıt alanını karşılaştırmak kaçan bozuğu 0'a indirir. Yeniden üretim eşiği hiçbir yeni bozuk
  tutmaz, hat süresini 9.040 saniyeye ve yanlış blokeyi 10'a çıkarır.

#### Sonraki Adım

Köken belgesi kuruldu, kanıt ile beyan ayrıldı ve eşik yükseltmenin fiyatı yazıldı. Ortada duran
şey yedi beyan alanıdır ve bunların dördü doğrudan tek bir konuyu anlatır: yapının **hangi ortamda**
koştuğunu. Çalıştırıcı türü, yalıtım düzeyi, ağ erişimi ve ortam değişkeni adları belgede yazar,
hiçbiri sınanamaz — ve sınanamadıkları için değerleri yalnız o ortamın gerçekten öyle
kurulmuş olmasına bağlıdır.

Sonraki ders o ortamı kalem kalem sayar. Ölçü artık belgenin alanı değil, derleme ortamının kendi
yetkisidir: hat kaç sırra erişiyor, kaçı gerçekten gerekiyor, ağ erişimi kapatıldığında kaç adım
kırılıyor ve dış katkıdan gelen bir değişiklik koştuğunda kaç sır açığa çıkabiliyor?

### Derleme Ortamı Yalıtımı

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/derleme-ortami-yalitimi

Derleme ortamının yetkisi kalem kalem sayılır: hat kaç kaleme erişiyor, kaçını gerçekten gereksiniyor, yalıtımdan sonra kaç erişim çifti kalıyor ve neden kalıyor, ağ tümden kapatıldığında kaç adım kırılıyor, depo dışından gelen bir değişiklik koştuğunda kaç sır erişilebilir durumda oluyor.

Önceki ders köken belgesinin on dört alanından yedisinin hiçbir koşulda sınanamadığını saydı.
O yedinin dördü tek bir konuyu anlatıyordu: çalıştırıcı türü, yalıtım düzeyi, ağ erişimi ve
ortam değişkeni adları. Dördü de belgede yazıyordu, dördü de beyandı ve değerleri yalnız o
ortamın gerçekten öyle kurulmuş olmasına bağlıydı.

Bu ders o ortamı kalem kalem sayar. Ölçü, derleme ortamının **eriştiği** kalem sayısı ile
**gerçekten gereksindiği** kalem sayısı arasındaki farktır. Sürekli Tümleştirme ve Teslimat
kursunda hat güvenliği ve sır yönetimi dağıtım ekseninde ölçülmüştü; o ölçüm tekrarlanmaz.
Buradaki nesne hattın kendi yetkisidir. Sayım savunma tarafındadır: hangi kalemin hangi adımdan
erişilebilir durumda olduğu envanterden hesaplanır, erişimin nasıl kullanılacağı yazılmaz.

**TZ15.** Kurgu hat beş adımdır ve erişim kalemleri dört türde on sekiz kalemdir; küme `node`
ile modellenir. Her kalemin hangi adım tarafından gerçekten gereksinildiği de kurgudur.
**TZ16.** Paylaşılan çalıştırıcıda her adım her kaleme erişir; ölçü birimi **erişim çiftidir**
— bir adımın bir kaleme erişebilir olması bir çifttir.

#### Derleme Ortamı Neye Erişiyor

```js
// olcum-agi/erisim-envanteri.mjs — derleme ortami hangi kaleme erisiyor, kaci gerekiyor
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
mkdirSync(KOK, { recursive: true });

// Kurgu bolgesel olcum agi: abone portalinin hatti bes adimdan olusur.
const ADIM = ["bagimlilik-cek", "derle", "sina", "paketle", "yayimla"];

// Kalem envanteri modeldir: tur, ad ve kalemi GERCEKTEN gereksinen adimlar.
const KALEM = [
  ["sir", "yapi-ciktisi-deposu-jetonu", ["yayimla"]],
  ["sir", "kaynak-deposu-anahtari", []],
  ["sir", "imza-gizli-anahtari", ["yayimla"]],
  ["sir", "bulut-kimligi", []],
  ["sir", "veritabani-parolasi", ["sina"]],
  ["sir", "bildirim-jetonu", ["yayimla"]],
  ["ag", "paket-aynasi", ["bagimlilik-cek"]],
  ["ag", "yapi-ciktisi-deposu", ["yayimla"]],
  ["ag", "imza-hizmeti", ["yayimla"]],
  ["ag", "genel-internet", []],
  ["ag", "ic-servis-agi", ["sina"]],
  ["yol", "kaynak-agaci", ["derle", "sina", "paketle"]],
  ["yol", "onbellek", ["bagimlilik-cek", "derle"]],
  ["yol", "onceki-is-artiklari", []],
  ["yol", "calistirici-kok", []],
  ["kimlik", "hat-hesabi", ["yayimla"]],
  ["kimlik", "kap-kayit-defteri", ["paketle"]],
  ["kimlik", "yonetim-duzlemi", []],
];
writeFileSync(join(KOK, "kalemler.json"), JSON.stringify({ ADIM, KALEM }));

// Paylasilan calistiricida her adim her kaleme erisir: erisim cifti = adim x kalem.
const TUR = ["sir", "ag", "yol", "kimlik"];
const S = [10, 8, 16, 15, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["tur", "kalem", "hic gerekmeyen", "erisim cifti", "gerekli cift"]);
let toplamCift = 0, toplamGerekli = 0, hicGereken = 0;
for (const t of TUR) {
  const kume = KALEM.filter(([tur]) => tur === t);
  const cift = kume.length * ADIM.length;
  const gerekli = kume.reduce((n, [, , g]) => n + g.length, 0);
  const bos = kume.filter(([, , g]) => g.length === 0).length;
  toplamCift += cift; toplamGerekli += gerekli; hicGereken += bos;
  satir([t, kume.length, bos, cift, gerekli]);
}
console.log("");
console.log(`adim ${ADIM.length}, kalem ${KALEM.length}; erisim cifti ${toplamCift}, ` +
  `gerekli ${toplamGerekli}, gereksiz ${toplamCift - toplamGerekli}`);
console.log(`hicbir adimin gereksinmedigi kalem ${hicGereken}: ` +
  KALEM.filter(([, , g]) => g.length === 0).map(([, a]) => a).join(", "));
```

```
tur       kalem   hic gerekmeyen  erisim cifti   gerekli cift
sir       6       2               30             4
ag        5       1               25             4
yol       4       2               20             5
kimlik    3       1               15             2

adim 5, kalem 18; erisim cifti 90, gerekli 15, gereksiz 75
hicbir adimin gereksinmedigi kalem 6: kaynak-deposu-anahtari, bulut-kimligi, genel-internet, onceki-is-artiklari, calistirici-kok, yonetim-duzlemi
```

Paylaşılan bir çalıştırıcıda beş adım on sekiz kaleme erişir ve bu **90 erişim çifti** eder.
Bu çiftlerin **15'i gereklidir**; kalan 75'i hiçbir adımın işine yaramaz ve hiçbiri bir karar
sonucu değildir. Kimse "derleme adımı imza gizli anahtarını görsün" demedi; adımlar aynı
çalıştırıcıda koştuğu için gördü.

En keskin sayı üçüncü sütundadır: on sekiz kalemin **altısı hiçbir adım tarafından
gereksinilmiyor**. Kaynak deposu anahtarı ile bulut kimliği hattın önceki bir sürümünden
kalmıştır, genel internet erişimi hiçbir adımın hedef listesinde yoktur, önceki iş artıkları ile
çalıştırıcı kökü paylaşılan makinenin kendi içeriğidir, yönetim düzlemi kimliği ise hiç
kullanılmamış geniş kapsamlı bir jetondur. Altı kalem hattın hiçbir işine yaramadan hattın
tamamında duruyor.

Bu altı kalem envanteri tutulduğu anda görülür. Ölçünün asıl işi de budur: yetki daraltma bir
ilke olarak söylendiğinde karar taşımaz, **çift olarak sayıldığında** hangi kalemin hangi adımda
ne işe yaradığı yazılabilir hale gelir. Bir kalemin gereksinen adım listesi boşsa o kalem
düşürülür; boş değilse liste zaten daraltmanın hedefidir.

#### Yalıtımdan Sonra Kalanlar

Yalıtım iki ayrı iş yapar: gereksinilmeyen kalemi tümden düşürmek ve kalan kalemin erişimini
gereksinen adıma daraltmak. İkincisi her kalemde aynı ölçüde mümkün değildir.

**TZ17.** Daraltmanın bağlandığı düzey kalemin veriliş biçimine göre değişir ve iki değer alır:
**adım düzeyi** ya da **iş düzeyi**. Bu bir modeldir. **TZ18.** Süreler veri olarak verilmiştir,
ölçülmüş değildir: iş başına yeni çalıştırıcı 38 saniye, sır kapsamı daraltma 6 saniye, ağ izin
listesi 2 saniye.

```js
// olcum-agi/yalitim.mjs — yalitimdan sonra kac kalem kaliyor ve neden kaliyor
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const { ADIM, KALEM } = JSON.parse(readFileSync(join(KOK, "kalemler.json")));

// Daraltmanin BAGLANDIGI duzey kalemin verilis bicimine gore degisir; bu bir modeldir.
// "adim": kalem yalniz gereksinen adima verilebilir. "is": duzenek is duzeyinde baglanir.
const DUZEY = {
  "yapi-ciktisi-deposu-jetonu": "adim", "imza-gizli-anahtari": "adim",
  "bildirim-jetonu": "adim", "kap-kayit-defteri": "adim", "hat-hesabi": "adim",
  "veritabani-parolasi": "is",          // ortam degiskeni olarak isin tamamina konuyor
  "paket-aynasi": "is", "yapi-ciktisi-deposu": "is",  // izin listesi calistirici duzeyinde
  "imza-hizmeti": "is", "ic-servis-agi": "is",
  "kaynak-agaci": "is", "onbellek": "is",             // calisma alani baglamasi is duzeyinde
};

// Yalitim iki is yapar: gereksinilmeyen kalem tumden dusurulur, kalan kalem daraltilir.
const kalan = KALEM.filter(([, , g]) => g.length > 0);
const erisen = ([, ad, g]) => (DUZEY[ad] === "adim" ? g.length : ADIM.length);

const TUR = ["sir", "ag", "yol", "kimlik"];
const S = [10, 18, 12, 14, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["tur", "kalem once/sonra", "cift once", "cift sonra", "kalan gereksiz"]);
let once = 0, sonra = 0, gerekli = 0;
for (const t of TUR) {
  const hepsi = KALEM.filter(([tur]) => tur === t);
  const k = kalan.filter(([tur]) => tur === t);
  const c1 = hepsi.length * ADIM.length;
  const c2 = k.reduce((n, e) => n + erisen(e), 0);
  const g = k.reduce((n, [, , gg]) => n + gg.length, 0);
  once += c1; sonra += c2; gerekli += g;
  satir([t, `${hepsi.length}/${k.length}`, c1, c2, c2 - g]);
}
console.log("");
console.log(`erisim cifti ${once} -> ${sonra}; gerekli ${gerekli}, ` +
  `kalan gereksiz ${sonra - gerekli}`);
console.log("is duzeyinde bagli kalan: " +
  kalan.filter(([, ad]) => DUZEY[ad] === "is").map(([, ad]) => ad).join(", "));

// Ag tumden kapatilsaydi: bir ag kalemi gereksinen her adim kirilir.
const agliAdim = [...new Set(kalan.filter(([t]) => t === "ag").flatMap(([, , g]) => g))];
console.log(`ag tumden kapatilirsa kirilan adim ${agliAdim.length}/${ADIM.length}: ` +
  agliAdim.join(", "));

// Sureler VERI olarak verilmistir, olculmus degil.
const [YENI_CALISTIRICI, SIR_DARALTMA, IZIN_LISTESI, KOSUM] = [38, 6, 2, 30];
const ek = YENI_CALISTIRICI + SIR_DARALTMA + IZIN_LISTESI;
console.log(`yalitimin kosum basina ek suresi ${ek} s (${YENI_CALISTIRICI}+${SIR_DARALTMA}+` +
  `${IZIN_LISTESI}), ${KOSUM} kosumda ${ek * KOSUM} s; sureler veri olarak verildi`);
```

```
tur       kalem once/sonra  cift once   cift sonra    kalan gereksiz
sir       6/4               30          8             4
ag        5/4               25          20            16
yol       4/2               20          10            5
kimlik    3/2               15          2             0

erisim cifti 90 -> 40; gerekli 15, kalan gereksiz 25
is duzeyinde bagli kalan: veritabani-parolasi, paket-aynasi, yapi-ciktisi-deposu, imza-hizmeti, ic-servis-agi, kaynak-agaci, onbellek
ag tumden kapatilirsa kirilan adim 3/5: bagimlilik-cek, yayimla, sina
yalitimin kosum basina ek suresi 46 s (38+6+2), 30 kosumda 1380 s; sureler veri olarak verildi
```

Erişim çifti **90'dan 40'a** iner ve gerekli olan 15'tir. Kazanç gerçektir: on sekiz kalemin
altısı düşürülür, kalan on ikisinin erişimi daraltılır. Ama kalan gereksiz çift sayısı sıfır
değil **25**'tir ve bu satır dersin asıl sorusudur: kalanlar neden kaldı?

Cevap türlere göre değişir. Kimlik satırında kalan gereksiz sıfırdır, çünkü jeton adıma özgü
verilebilir: yapı hesabı yalnız yayımlama adımında, kayıt defteri kimliği yalnız paketleme
adımında durur. Sır satırında kalan gereksiz dörttür ve dördü tek bir kalemden gelir —
veritabanı parolası sınama adımına ortam değişkeni olarak veriliyor, ortam değişkeni ise işin
tamamına konuyor. Bir tek kalemin veriliş biçimi, o kalemi beş adımın hepsine açıyor.

Ağ satırı en kötüsüdür: dört hedef kalır, hepsi iş düzeyinde bağlıdır ve **16 gereksiz çift**
üretir. Nedeni ağ izin listesinin çalıştırıcı düzeyinde uygulanmasıdır; liste "bu iş şu dört
hedefe çıkabilir" der, "derleme adımı yalnız paket aynasına çıkabilir" diyemez. Aynı biçimde
çalışma alanı bağlaması iş düzeyindedir: kaynak ağacı ile önbellek, gereksinmeyen adımlara da
açık kalır. **Daraltmanın sınırı ilkede değil, kalemin veriliş düzeneğindedir.**

Üçüncü çıktı satırı bir öncekiyle bağ kurar. Köken belgesinde ağ erişimi alanı "kapalı" yazıyordu
ve sınanamıyordu. Burada sayı şunu söylüyor: ağ tümden kapatılırsa **beş adımın üçü kırılır** —
bağımlılık çekme, sınama ve yayımlama. O alan yalnız doğrulanamaz değil, yazıldığı biçimde doğru
da olamaz. Doğru olabilecek tek biçim "dört hedefe izinli" gibi bir listedir ve o liste bir
kanıt değil, yine bir beyandır.

Son satır bedeli yazar. Yalıtım koşum başına 46 saniye ekler, otuz koşumda **1.380 saniye**.
Bu sürenin karşılığı 50 erişim çiftinin kapanmasıdır; kalan 25 çift için ödenen bir şey yoktur,
çünkü onlar süreyle değil düzenekle kapanır.

#### Depo Dışından Gelen Değişiklik

Erişim çifti kimin değişikliğinin koştuğuna bakmaz. Aynı kalemler, deponun içinden gelen bir
değişiklik için de dışından gelen bir katkı için de aynı adımlara açıktır.

**TZ19.** Otuz koşumun sekizi depo dışından gelen bir değişiklikle tetiklenir; yalıtılmış
yapılandırmalarda dış katkıda yayımlama adımı atlanır. Bir adım, gereksindiği kalemlerden biri
o hatta hiç yoksa koşamaz.

```js
// olcum-agi/dis-katki.mjs — depo disindan gelen bir degisiklik kostugunda kac sir erisilebilir
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const { ADIM, KALEM } = JSON.parse(readFileSync(join(KOK, "kalemler.json")));
const DUZEY = {
  "yapi-ciktisi-deposu-jetonu": "adim", "imza-gizli-anahtari": "adim",
  "bildirim-jetonu": "adim", "kap-kayit-defteri": "adim", "hat-hesabi": "adim",
  "veritabani-parolasi": "is", "paket-aynasi": "is", "yapi-ciktisi-deposu": "is",
  "imza-hizmeti": "is", "ic-servis-agi": "is", "kaynak-agaci": "is", "onbellek": "is",
};

// Kurgu: otuz kosumun sekizi depo disindan gelen bir degisiklikle tetikleniyor.
const KOSUM = 30, DIS = 8;
const kalan = KALEM.filter(([, , g]) => g.length > 0);
const SIR = KALEM.filter(([t]) => t === "sir").map(([, ad]) => ad);

// Uc yapilandirma: ic degisiklik kumesi, dis katki kumesi, daraltma, atlanan adim.
const YAPI = [
  ["paylasilan calistirici", KALEM, KALEM, false, []],
  ["yalitilmis hat", kalan, kalan, true, ["yayimla"]],
  ["dis katkiya sirsiz hat", kalan,
    kalan.filter(([t, ad]) => t !== "sir" && ad !== "ic-servis-agi"), true, ["yayimla"]],
];

// Bir kalem, erisim duzeyi ile kosan adimlarin kesisimi bos degilse erisilebilirdir.
const erisilir = (kume, daralt, kosan, ad) => {
  const kalem = kume.find(([, a]) => a === ad);
  if (kalem === undefined) return false;
  return (daralt && DUZEY[ad] === "adim" ? kalem[2] : ADIM).some((a) => kosan.includes(a));
};

const S = [26, 10, 11, 17, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["yapilandirma", "ic: sir", "dis: sir", "dis: kosmayan", "sekiz katkida sir-kosum"]);
const sonuc = [];
for (const [ad, icKume, disKume, daralt, atlanan] of YAPI) {
  const disKosan = ADIM.filter((a) => !atlanan.includes(a));
  const ic = SIR.filter((s) => erisilir(icKume, daralt, ADIM, s)).length;
  const dis = SIR.filter((s) => erisilir(disKume, daralt, disKosan, s)).length;
  // Bir adim, gereksindigi kalemlerden biri o hatta hic yoksa kosamaz.
  const kirik = disKosan.filter((a) =>
    KALEM.some(([, kad, g]) => g.includes(a) && !disKume.some(([, b]) => b === kad)));
  sonuc.push([ad, kirik]);
  satir([ad, ic, dis, [...atlanan, ...kirik].join(", ") || "-", dis * DIS]);
}
console.log("");
console.log(`kosum ${KOSUM}, depo disindan gelen ${DIS}; hattaki sir ${SIR.length}, ` +
  `yalitimdan sonra kalan ${kalan.filter(([t]) => t === "sir").length}`);
const [sonAd, sonKirik] = sonuc.at(-1);
console.log(`${sonAd}: kosamayan adim ${sonKirik.join(", ")}; ${DIS} dis katki ` +
  `entegrasyon sinamasi gormeden inceleme kuyruguna giriyor`);
```

```
yapilandirma              ic: sir   dis: sir   dis: kosmayan    sekiz katkida sir-kosum
paylasilan calistirici    6         6          -                48
yalitilmis hat            4         1          yayimla          8
dis katkiya sirsiz hat    4         0          yayimla, sina    0

kosum 30, depo disindan gelen 8; hattaki sir 6, yalitimdan sonra kalan 4
dis katkiya sirsiz hat: kosamayan adim sina; 8 dis katki entegrasyon sinamasi gormeden inceleme kuyruguna giriyor
```

İlk satır sayıyı en yalın biçimde verir: paylaşılan çalıştırıcıda depo dışından gelen bir
değişiklik **altı sırrın altısını** erişilebilir bulur ve sekiz katkı boyunca bu **48 sır-koşum
çifti** eder. Değişikliğin ne yaptığı önemli değildir; ortam onu kendi değişikliğinden ayırmaz,
çünkü ayırmasını sağlayacak bir kalem yoktur.

İkinci satırda erişilebilen sır **1**'e iner ve düşüşün kaynağı ikiye bölünür. İki sır zaten
düşürülmüştür, üçü yayımlama adımına bağlıdır ve dış katkıda o adım koşmaz. Geriye kalan tek
sır yine veritabanı parolasıdır: iş düzeyinde bağlı olduğu için sınama adımıyla birlikte
gelir. Yalıtımın bu yapılandırmadaki kazancı 48 çiftten 8 çifte inmektir ve kalan 8'in
tamamı **tek bir kalemin veriliş biçiminden** doğar.

Üçüncü satır o kalemi de kaldırır ve bedeli hemen görünür. Sırsız hatta erişilebilen sır sıfırdır,
ama sınama adımı koşamaz — veritabanı parolası ve iç servis ağı orada yoktur. Sekiz dış katkı
entegrasyon sınaması görmeden inceleme kuyruğuna girer. Bu, yalıtımın işe yaramadığı yer
değildir; **fiyatının başka bir kalemden ödendiği** yerdir. Sıfır erişim ile sınanmış katkı
aynı anda elde edilmiyor ve ikisi arasındaki seçim sekiz katkının ne kadar incelendiğine bağlı
bir karardır, bir ilke sorusu değil.

#### Özet

- Beş adımlı hat on sekiz kaleme erişir: 90 erişim çiftinin yalnız 15'i gereklidir. On sekiz
  kalemin 6'sı hiçbir adım tarafından gereksinilmez ve hattın tamamında durur.
- Yalıtım erişim çiftini 90'dan 40'a indirir; 25 çift gereksiz kalır. Kimlik türünde kalan
  gereksiz 0, ağ türünde 16'dır.
- Kalanların nedeni ilkede değil düzenektedir: ağ izin listesi çalıştırıcı düzeyinde, çalışma
  alanı bağlaması iş düzeyinde uygulanır, bir sır ortam değişkeni olarak işin tamamına konur.
- Ağ tümden kapatılırsa beş adımın 3'ü kırılır; köken belgesindeki "ağ erişimi kapalı" beyanı
  yalnız doğrulanamaz değil, yazıldığı biçimde doğru da olamaz. Yalıtım koşum başına 46 saniye,
  otuz koşumda 1.380 saniye ekler.
- Depo dışından gelen değişiklik paylaşılan çalıştırıcıda 6 sırra erişir (sekiz katkıda 48 çift),
  yalıtılmış hatta 1 sırra (8 çift), sırsız hatta 0 sırra — ama sırsız hatta sınama adımı koşmaz
  ve sekiz katkı sınanmadan incelemeye girer.

#### Sonraki Adım

Derleme ortamının yetkisi sayıldı: kaç kaleme erişiliyor, kaçı gerekiyor, yalıtımdan sonra ne
kalıyor ve neden kalıyor. Bütün bu ölçüm hattın **kendi** kalemleri üzerineydi. Oysa aynı hattın
ilk adımı bağımlılık çekmedir ve o adım, envanterdeki yetmiş üç kaydı hatta getiren adımdır.
Yetki daraltma o kayıtların hiçbirine dokunmaz: bir bileşen hatta girdiğinde ortamın ne kadar
dar olduğu değil, o bileşenin ne olduğu belirleyicidir.

Sonraki ders bu soruyu bir kural kümesine çevirir. Envanterdeki kayıtlar bir politikadan
geçirilir ve ölçü yine iki sayıdır: politika kaç bileşeni işaretliyor ve işaretlerin kaçı
yanlış? Terkedilmiş paket kuralı en çok işaret üreten kuraldır; ürettiği işaretlerin kaçı
gerçekten bir sorunu gösteriyor, kaçı yalnız bir tarih alanının eskimesini gösteriyor?

### Bağımlılık Politikası

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/bagimlilik-politikasi

Bağımlılık politikası kural kümesi olarak koşturulur ve iki sayı ayrılır: yedi kuralın kaçı envanterin alanlarıyla koşabiliyor, kırk altı işaret kaç bileşene düşüyor, işaretlerin kaçı gerçek ve kaçı yanlış, hiçbir kuralın görmediği aykırı bileşen kaç tane ve kural kapıya dönüştüğünde kaç yapı yanlış yere duruyor.

Önceki ders derleme ortamının yetkisini kalem kalem saydı ve hattın ilk adımını dışarıda bıraktı.
Bağımlılık çekme adımı envanterdeki yetmiş üç kaydı hatta getiren adımdır ve yetki daraltma o
kayıtların hiçbirine dokunmaz. Ortam ne kadar dar olursa olsun, içeri giren bileşenin ne olduğu
ayrı bir sorudur.

Bu ders o soruyu bir kural kümesine çevirir. Ölçü iki sayıdır: politika kaç bileşeni
işaretliyor ve işaretlerin kaçı yanlış. Bağımlılık riski ve sürüm sabitleme daha önce iki kursta
ölçülmüştü; o pratik tekrarlanmaz. Buradaki nesne kural kümesinin **kendisidir** — hangi kural
hangi alanla koşuyor ve işaretinin karşılığı ne.

**TZ20.** Envanter ilk dersteki modelle aynı tohumdan yeniden üretilir: 73 kayıt, 48 benzersiz
bileşen. Politika bileşen üzerinde koşar; kayıtlar benzersiz ad altında toplanır. **TZ21.**
Politika iki kaynağı birleştirir: envanterin beş alanı ve dış paket kaydının iki alanı — son
yayın ayı ve bakımcı sayısı. İkisi de envanterde yoktur; dış kaydın tohumu `660418` görünürdür.

#### Politikayı Kural Olarak Koşturmak

```js
// olcum-agi/politika.mjs — bagimlilik politikasi kural olarak kosturulur: kac bileseni isaretliyor
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
mkdirSync(KOK, { recursive: true });
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(20260731);                                  // envanter tohumu gorunur
const sec = (d) => d[Math.floor(r() * d.length)];

// Envanter ilk dersteki modelle ayni tohumdan yeniden uretilir: 73 kayit, 48 benzersiz bilesen.
const DEPO = [["abone-portali", 11], ["sayac-alim", 9], ["ortak-kitaplik", 6]];
const HAVUZ = Array.from({ length: 70 }, (_, i) => `bilesen-${String(i + 1).padStart(2, "0")}`);
const SURUM = ["1.2", "1.9", "2.0", "2.4", "3.0", "3.6", "4.1"];
const LISANS = ["izin-veren", "izin-veren", "izin-veren", "zayif-karsilikli", "karsilikli"];
const kayit = [];
for (const [depo, dogrudanSayi] of DEPO) {
  const alinan = new Set();
  const yeni = () => { let a; do { a = sec(HAVUZ); } while (alinan.has(a)); alinan.add(a); return a; };
  const ekle = (ad, getiren) => kayit.push({ ad, surum: sec(SURUM), depo,
    lisans: r() < 0.18 ? "" : sec(LISANS),
    getiren: getiren === null ? depo : (r() < 0.12 ? "" : getiren) });
  for (let i = 0; i < dogrudanSayi; i++) {
    const kok = yeni();
    ekle(kok, null);
    for (let k = 0, n = Math.floor(r() * 4); k < n; k++) {
      const cocuk = yeni();
      ekle(cocuk, kok);
      if (r() < 0.35) ekle(yeni(), cocuk);
    }
  }
}

// Bilesen gorunumu: yetmis uc kayit benzersiz ad altinda toplanir.
const DEPO_AD = new Set(DEPO.map(([ad]) => ad));
const BILESEN = new Map();
for (const k of kayit) {
  const b = BILESEN.get(k.ad) ?? { ad: k.ad, lisans: "", dogrudan: false, getirenBos: false };
  if (b.lisans === "") b.lisans = k.lisans;
  if (DEPO_AD.has(k.getiren)) b.dogrudan = true;
  if (k.getiren === "") b.getirenBos = true;
  BILESEN.set(k.ad, b);
}

// Dis paket kaydi ayri bir kaynaktir: son yayin ayi ve bakimci sayisi envanterde yoktur.
const d = uretec(660418);                                    // dis kayit tohumu gorunur
for (const b of BILESEN.values()) {
  b.disKayit = d() < 0.9;
  b.sonYayinAy = 1 + Math.floor(d() * 47);
  b.bakimci = 1 + Math.floor(d() * d() * 5);
  b.yerineGecenVar = d() < 0.55;                             // gizli: kurallar bunu gormez
  b.devredilmis = d() < 0.07;                                // gizli: hicbir alan gostermez
  b.gizliLisans = b.lisans === "" ? (d() < 0.45 ? "karsilikli" : "izin-veren") : b.lisans;
}
const B = [...BILESEN.values()];

// Yedi kural. Her kural gereksindigi alani bildirir; alan yoksa kural kosamaz.
const ALAN = new Set(["ad", "surum", "depo", "lisans", "getiren", "sonYayinAy", "bakimci"]);
const KURAL = [
  ["kisitli lisans", ["lisans"], (b) => b.lisans === "karsilikli"],
  ["lisans bilinmiyor", ["lisans"], (b) => b.lisans === ""],
  ["terkedilmis paket", ["sonYayinAy", "bakimci"],
    (b) => b.disKayit && b.sonYayinAy >= 24 && b.bakimci <= 1],
  ["tek bakimci", ["bakimci"], (b) => b.disKayit && b.bakimci === 1],
  ["dis kayitta karsiligi yok", ["sonYayinAy"], (b) => b.disKayit === false],
  ["getiren kenari bos", ["getiren"], (b) => b.getirenBos],
  ["kosumda cagrilan kisitli lisansli", ["cagrilir", "lisans"], null],
];

const S = [36, 22, 11, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["kural", "gereken alan", "kosabildi", "isaretlenen bilesen"]);
const isaret = {};
for (const [ad, gereken, sina] of KURAL) {
  const kosar = gereken.every((a) => ALAN.has(a));
  if (kosar) isaret[ad] = B.filter(sina).map((b) => b.ad);
  satir([ad, gereken.join("+"), kosar ? "evet" : "HAYIR",
    kosar ? isaret[ad].length : "alan yok"]);
}
const birlesim = new Set(Object.values(isaret).flat());
writeFileSync(join(KOK, "politika.json"), JSON.stringify({ B, isaret }));
console.log("");
console.log(`kayit ${kayit.length}, benzersiz bilesen ${B.length}; dis kayitta karsiligi olan ` +
  `${B.filter((b) => b.disKayit).length}; tohum 20260731 ve 660418`);
console.log(`yedi kuralin ${Object.keys(isaret).length}'si kosuyor, biri envanterde alan ` +
  `olmadigi icin kosmuyor; isaret ${Object.values(isaret).flat().length}, ` +
  `isaretlenen benzersiz bilesen ${birlesim.size}/${B.length}`);
```

```
kural                               gereken alan          kosabildi  isaretlenen bilesen
kisitli lisans                      lisans                evet       5
lisans bilinmiyor                   lisans                evet       6
terkedilmis paket                   sonYayinAy+bakimci    evet       9
tek bakimci                         bakimci               evet       18
dis kayitta karsiligi yok           sonYayinAy            evet       2
getiren kenari bos                  getiren               evet       6
kosumda cagrilan kisitli lisansli   cagrilir+lisans       HAYIR      alan yok

kayit 73, benzersiz bilesen 48; dis kayitta karsiligi olan 46; tohum 20260731 ve 660418
yedi kuralin 6'si kosuyor, biri envanterde alan olmadigi icin kosmuyor; isaret 46, isaretlenen benzersiz bilesen 30/48
```

Yedi kuralın **altısı koşar, biri koşamaz**. Koşamayanın nedeni bir yapılandırma eksiği değildir:
"koşumda çağrılan kısıtlı lisanslı bileşen" kuralı `cagrilir` alanını gereksinir ve o alan ne
envanterde ne dış kayıttadır. İlk ders bu soruyu cevaplanmayan dört sorudan biri olarak saymıştı;
burada aynı boşluk bir kuralın hiç yazılamaması olarak görünür. **Bir politika, dayandığı alan
kümesi kadar yazılabilir.**

Koşan altı kural **46 işaret** üretir ve bu işaretler 48 bileşenin **30'una** düşer: envanterin
üçte ikisi işaretlenmiştir. Dağılım eşit değildir — tek bakımcı kuralı tek başına 18 işaret
üretir. Birleştirme de kusursuz değildir: iki bileşen dış kayıtta hiç bulunmaz ve o ikisi için
terkedilmişlik ile bakımcı sayısı kuralları **hiç değerlendirilemez**.

#### İşaretin Kaçı Yanlış

Otuz işaretli bileşen tek başına bir sayı değildir. Ölçünün ikinci yarısı, bu işaretlerin kaçının
gerçekten bir politika aykırılığına karşılık geldiğidir.

**TZ22.** Gerçekten aykırı küme kurgudur ve dört kaynaktan gelir: karşılıklı lisanslı doğrudan
bağımlılık, yerine geçen sürümü olmayan terkedilmiş paket, lisansı bilinmeyen ama gerçekte
kısıtlı olan bileşen ve bakımı devredilmiş paket. Kurallar bu kümeyi görmez. Devir üç bileşende
olmuştur ve **devri gösteren alan hiçbir kaynakta yoktur**; bu üçü kuralların işaretlemediği
kümeden seçilir.

```js
// olcum-agi/yanlis-isaret.mjs — politikanin isaretlerinin kaci gercek, kaci yanlis
import { readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const { B, isaret } = JSON.parse(readFileSync(join(KOK, "politika.json")));
const birlesim = new Set(Object.values(isaret).flat());

// Kurgu gercek durum. Kurallar bu kumeyi gormez; gizli alanlar yalniz burada okunur.
const GERCEK = new Set();
const SEBEP = { lisans: 0, bakimsiz: 0, gizliLisans: 0, devir: 0 };
for (const b of B) {
  // (a) Karsilikli lisansli ve dogrudan bagimlilik: lisans yukumlulugu gercekten doguyor.
  if (b.lisans === "karsilikli" && b.dogrudan) { GERCEK.add(b.ad); SEBEP.lisans++; }
  // (b) Terkedilmis ve yerine gecen surumu olmayan paket: bakimsizlik gercekten risk.
  else if (b.disKayit && b.sonYayinAy >= 24 && b.bakimci <= 1 && !b.yerineGecenVar) {
    GERCEK.add(b.ad); SEBEP.bakimsiz++;
  }
  // (c) Lisansi bilinmeyen ama gercekte kisitli olan bilesen: bos alanin arkasindaki durum.
  else if (b.lisans === "" && b.gizliLisans === "karsilikli") {
    GERCEK.add(b.ad); SEBEP.gizliLisans++;
  }
}
// (d) Kurgu: hicbir kuralin isaretlemedigi kumeden uc bilesende bakim devri olmustur.
// Devri gosteren alan ne envanterde ne dis kayitta vardir: kurallarin gorus alani disi.
const temizGorunen = B.filter((b) => !birlesim.has(b.ad)).map((b) => b.ad);
for (const ad of [0, 6, 12].map((i) => temizGorunen[i])) { GERCEK.add(ad); SEBEP.devir++; }

writeFileSync(join(KOK, "gercek.json"), JSON.stringify([...GERCEK]));

const S = [30, 9, 9, 15, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["kural", "isaret", "gercek", "yanlis isaret", "yanlis oran"]);
for (const [ad, liste] of Object.entries(isaret)) {
  const gercek = liste.filter((a) => GERCEK.has(a)).length;
  satir([ad, liste.length, gercek, liste.length - gercek,
    `%${Math.round((100 * (liste.length - gercek)) / liste.length)}`]);
}

const gorulen = [...GERCEK].filter((a) => birlesim.has(a));
const gorulmeyen = [...GERCEK].filter((a) => !birlesim.has(a));
console.log("");
console.log(`gercekten aykiri ${GERCEK.size}/${B.length} bilesen: lisans ${SEBEP.lisans}, ` +
  `bakimsiz ${SEBEP.bakimsiz}, bilinmeyen lisans ${SEBEP.gizliLisans}, devir ${SEBEP.devir}`);
console.log(`isaret ${Object.values(isaret).flat().length}, isaretlenen benzersiz bilesen ` +
  `${birlesim.size}: ${gorulen.length} gercek, ${birlesim.size - gorulen.length} yanlis isaret`);
console.log(`hicbir kuralin gormedigi gercek aykiri ${gorulmeyen.length}/${GERCEK.size}; ` +
  `temiz gorunen ${temizGorunen.length} bilesenin icinden`);
```

```
kural                         isaret   gercek   yanlis isaret  yanlis oran
kisitli lisans                5        3        2              %40
lisans bilinmiyor             6        1        5              %83
terkedilmis paket             9        6        3              %33
tek bakimci                   18       7        11             %61
dis kayitta karsiligi yok     2        0        2              %100
getiren kenari bos            6        1        5              %83

gercekten aykiri 13/48 bilesen: lisans 3, bakimsiz 6, bilinmeyen lisans 1, devir 3
isaret 46, isaretlenen benzersiz bilesen 30: 10 gercek, 20 yanlis isaret
hicbir kuralin gormedigi gercek aykiri 3/13; temiz gorunen 18 bilesenin icinden
```

Otuz işaretli bileşenin **10'u gerçek, 20'si yanlış işarettir**. Politika envanterin üçte ikisini
işaretliyor, gerçekten aykırı olansa sekizde biridir.

Kural bazında dağılım bir sıralama verir. Terkedilmiş paket kuralı en az gürültülüsüdür: 9
işaretin 6'sı gerçektir, çünkü iki alanı birlikte arar — hem yayın yaşı hem bakımcı sayısı.
Yanlış üçü, yayını eskimiş ama yerine geçen sürümü bulunan paketlerdir; tarih alanının eskimesi
tek başına bakımsızlık anlamına gelmiyor. Tek bakımcı kuralı ise 18 işaretle en çok konuşan
kuraldır ve **11'i yanlıştır**: bakımcı sayısı bir aykırılık değil, bir dağılım özelliğidir.

Son satır ölçünün eksik yarısını yazar. Gerçekten aykırı 13 bileşenin **3'ü hiçbir kural
tarafından görülmez** ve üçü de kuralların "temiz" saydığı 18 bileşenin içindedir. Nedeni bir
eşik ayarı değildir: bakım devrini gösteren alan hiçbir kaynakta bulunmaz, dolayısıyla o üçünü
gösterecek bir kural **yazılamaz**. Yanlış negatifi yazılmayan bir politika ölçümü, işaretlediği
otuz bileşeni bir güvence gibi sunar.

#### Kural Kapı Olduğunda

Bir işaret listesiyle duran bir yapı aynı şey değildir. Durdurulan iş ancak kural bir kapıya
dönüştüğünde ölçülebilir.

**TZ23.** Otuz yapı kurgudur; her yapı bir ile dört bileşeni değiştirir, tohum `9143205`
görünürdür. Bir yapı yalnız yanlış işaretlerle duruyorsa ekip o bileşenleri istisna listesine
yazar ve bileşen bir daha durdurmaz. **TZ24.** Süreler veridir: değerlendirme 9 saniye, duran
yapı başına 4 dakika müdahale.

```js
// olcum-agi/kapi.mjs — politika kapi oldugunda: kac yapi duruyor, kaci yanlis yere duruyor
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const { B, isaret } = JSON.parse(readFileSync(join(KOK, "politika.json")));
const GERCEK = new Set(JSON.parse(readFileSync(join(KOK, "gercek.json"))));
const isaretli = new Map(B.map((b) => [b.ad, Object.entries(isaret)
  .filter(([, l]) => l.includes(b.ad)).map(([k]) => k)]));
const dogrudan = new Map(B.map((b) => [b.ad, b.dogrudan]));

const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
// Kurgu: otuz yapi, her biri bir ile dort bileseni degistiriyor. Tohum gorunur.
const r = uretec(9143205);
const YAPI = Array.from({ length: 30 }, () =>
  Array.from({ length: 1 + Math.floor(r() * 4) }, () => B[Math.floor(r() * B.length)].ad));

// Sureler VERI olarak verilmistir: degerlendirme 9 s, duran yapi basina mudahale 4 dk.
const [DEGER_S, MUDAHALE_DK] = [9, 4];
const KIP = [
  ["yalniz uyar", () => false],
  ["dogrudan bagimlilikta dur", (a) => dogrudan.get(a) && isaretli.get(a).length > 0],
  ["her isarette dur", (a) => isaretli.get(a).length > 0],
];

const S = [28, 11, 14, 11, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["kapi kipi", "duran yapi", "yanlis duran", "istisna", "hat + mudahale"]);
for (const [ad, durdurur] of KIP) {
  const istisna = new Set();
  let duran = 0, yanlisDuran = 0;
  for (const bilesenler of YAPI) {
    const sebep = bilesenler.filter((a) => durdurur(a) && !istisna.has(a));
    if (sebep.length === 0) continue;
    duran++;
    if (sebep.every((a) => !GERCEK.has(a))) {         // hepsi yanlis isaret: istisna yazilir
      yanlisDuran++;
      for (const a of sebep) istisna.add(a);
    }
  }
  satir([ad, duran, yanlisDuran, istisna.size,
    `${YAPI.length * DEGER_S} s + ${duran * MUDAHALE_DK} dk`]);
}

// Yanlis orani yuksek kural toptan bastirilirsa gorulen gercek kac tane kalir.
const ESIK = 0.6;
const kalanKural = Object.entries(isaret).filter(([, l]) =>
  l.filter((a) => !GERCEK.has(a)).length / l.length <= ESIK);
const kalanIsaret = new Set(kalanKural.flatMap(([, l]) => l));
const tumIsaret = new Set(Object.values(isaret).flat());
console.log("");
console.log(`otuz yapi ${YAPI.flat().length} bilesen dokunusu; isaretli bilesen ` +
  `${[...isaretli.values()].filter((k) => k.length > 0).length}/${B.length}; tohum 9143205`);
console.log(`yanlis orani %${ESIK * 100} ustundeki kural bastirilirsa: kural ` +
  `${Object.keys(isaret).length} -> ${kalanKural.length}, isaretlenen bilesen ` +
  `${tumIsaret.size} -> ${kalanIsaret.size}`);
console.log(`gorulen gercek aykiri ${[...GERCEK].filter((a) => tumIsaret.has(a)).length} -> ` +
  `${[...GERCEK].filter((a) => kalanIsaret.has(a)).length}/${GERCEK.size}`);
```

```
kapi kipi                   duran yapi yanlis duran  istisna    hat + mudahale
yalniz uyar                 0          0             0          270 s + 0 dk
dogrudan bagimlilikta dur   16         5             5          270 s + 64 dk
her isarette dur            24         9             14         270 s + 96 dk

otuz yapi 85 bilesen dokunusu; isaretli bilesen 30/48; tohum 9143205
yanlis orani %60 ustundeki kural bastirilirsa: kural 6 -> 2, isaretlenen bilesen 30 -> 14
gorulen gercek aykiri 10 -> 9/13
```

Üç satır aynı kural kümesinin üç ayarıdır. Yalnız uyaran kip hattan 270 saniye alır ve hiçbir
yapıyı durdurmaz; bulduğu her şey bir listeye yazılır ve listeyi okumak kimsenin işi değildir.
Her işarette duran kip otuz yapının **24'ünü** durdurur, 96 dakika geliştirici zamanı yer ve
**14 bileşenlik bir istisna listesi** bırakır. Doğrudan bağımlılıkla sınırlı kip ortadadır:
16 duran yapı, 5 yanlış duran, 5 istisna.

İstisna sütunu asıl sonucu taşır. On dört bileşen yanlış yere durdurdukları için listeye
yazılmıştır ve liste bir daha küçülmez. Kural kendi ürettiği yanlış işaretle kendini kapatır:
kırk altı işaretin yirmisi yanlışsa kapı, çalıştıkça etkisizleşen bir kapıdır.

Son iki satır bunun toplu biçimini ölçer. Yanlış oranı %60'ı geçen dört kural toptan
bastırılırsa işaretlenen bileşen **30'dan 14'e** iner ve görülen gerçek aykırı **10'dan 9'a**
düşer. Dört gürültülü kural, tek bir gerçek bileşen dışında yeni hiçbir şey göstermiyordu; listeden
düşen on altı bileşenin on beşi yanlış işaretti. **Bir kuralın değeri işaret sayısında değil,
kendinden başka hiçbir kuralın göstermediği gerçek kalem sayısındadır.**

#### Özet

- Yedi kuralın 6'sı koşar, biri koşamaz: `cagrilir` alanı ne envanterde ne dış kayıttadır. Bir
  politika, dayandığı alan kümesi kadar yazılabilir.
- Koşan altı kural 46 işaret üretir ve 48 bileşenin 30'una düşer; iki bileşenin dış kayıtta
  karşılığı yoktur ve o ikisi için iki kural hiç değerlendirilemez.
- İşaretlenen 30 bileşenin 10'u gerçek, 20'si yanlış işarettir. Terkedilmiş paket kuralı iki
  alanı birlikte aradığı için en az gürültülüsüdür (%33); tek bakımcı kuralı 18 işaretin 11'inde
  yanılır.
- Gerçekten aykırı 13 bileşenin 3'ü hiçbir kural tarafından görülmez, çünkü bakım devrini
  gösteren alan hiçbir kaynakta yoktur.
- Her işarette duran kip 30 yapının 24'ünü durdurur, 96 dakika müdahale ister ve 14 bileşenlik
  bir istisna listesi bırakır. Gürültülü dört kural bastırılırsa işaret 30'dan 14'e iner, görülen
  gerçek aykırı yalnız 10'dan 9'a düşer.

#### Sonraki Adım

Politika koşturuldu, işaretler sayıldı ve işaretin karşılığı yazıldı. Elde otuz işaretli bileşen,
on gerçek aykırılık ve on dört bileşenlik bir istisna listesi var. Bu kursun önceki dersleri de
kendi listelerini bıraktı: denetimlerin bulguları, bağımlılık eşleştirmesinin kalemleri,
envanterin eksik alanları, köken belgesinin sınanamayan beyanları. Hepsi ayrı listelerdir ve
hepsi aynı geliştirici kuyruğuna düşer.

Kuyruk sonlu olduğuna göre sıra bir yerden gelmek zorundadır. Sonraki ders bu bulguları tek bir
kümede toplar ve üç kez sıralar: önce ciddiyetle, sonra sömürülebilirlikle, sonra maruziyetle.
Ölçü sıralamanın kendisi değil, sıralamalar arasındaki farktır — ilk ona giren kalemler kaç yerde
değişiyor ve hangi kalem üçünde de üstte kalıyor?

### Bulgu Önceliklendirme

Source: https://academia.sh/tr/kurslar/guvenlik-otomasyonu/bulgu-onceliklendirme

Kursun bütün denetimlerinden gelen bulgular tek kümede toplanıp ciddiyet, sömürülebilirlik ve maruziyetle üç kez sıralanır; ölçü sıralamanın kendisi değil aralarındaki farktır: kaç çiftte sıra ters dönüyor, ilk ona kaç kalem üçünde de giriyor, en çok kayan kalem kaç sıra yer değiştiriyor ve sonlu kapasite hangi sıralamayla neyi dışarıda bırakıyor.

Politika koşturuldu ve elde otuz işaretli bileşen kaldı. Bu kursun önceki on ölçümü de kendi
listesini bıraktı: tarama bulguları, eşleştirme kalemleri, envanterin cevaplayamadığı sorular,
köken belgesinin sınanamayan alanları. Hepsi ayrı listelerdir ve hepsi aynı kuyruğa düşer.

Kuyruk sonlu olduğuna göre sıra bir yerden gelmek zorundadır. Bu ders üç ölçütü ayrı ayrı
uygular: kalemin kendi ağırlığı, çalışan bir yolun bulunup bulunmadığı ve kaç şeyin etkilendiği.
Test süreci kursunda tekil bulgunun elle doğrulanması ölçülmüştü; o puanlama tekrarlanmaz. Ölçü
sıralamanın kendisi değil, **sıralamalar arasındaki farktır.**

**TZ25.** Birleşik küme kurgudur: otuz dört bulgu tek listede toplanır ve `node` ile modellenir;
tohum `330941` görünürdür. **TZ26.** Ciddiyet doğrudan verilir; sömürülebilirlik üç özellikten
(çağrılıyor mu, dışarıdan erişilir mi, yetki gerekiyor mu), maruziyet üç özellikten (üretimde mi,
kaç servis, kaç uç) hesaplanır. Eşitlik bozucu bulgu numarasıdır.

#### Aynı Küme, Üç Sıralama

```js
// olcum-agi/siralama.mjs — ayni bulgu kumesi uc olcutle uc kez siralanir
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
mkdirSync(KOK, { recursive: true });
const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(330941);                                  // tohum gorunur

// Kurgu birlesik kume: kursun on bir olcumunden gelen otuz dort bulgu tek listede toplanir.
const KAYNAK = ["statik", "calisan uyg", "bagimlilik", "sir tarama",
  "imaj katmani", "sablon", "koken", "politika"];
const BULGU = Array.from({ length: 34 }, (_, i) => {
  const b = { no: i + 1, kaynak: KAYNAK[Math.floor(r() * KAYNAK.length)],
    ciddiyet: 3 + Math.floor(r() * 8),                     // olcut 1: kalemin kendi agirligi
    cagrilir: r() < 0.62, disaridan: r() < 0.45, yetkiGerekir: r() < 0.55,
    uretimde: r() < 0.7, servis: 1 + Math.floor(r() * 3), uc: 1 + Math.floor(r() * 40) };
  // Olcut 2: bu kod tabaninda calisan bir yol var mi. Olcut 3: kac sey etkileniyor.
  b.somuru = (b.cagrilir ? 4 : 0) + (b.disaridan ? 4 : 0) + (b.yetkiGerekir ? 0 : 2);
  b.maruziyet = (b.uretimde ? 4 : 0) + b.servis + (b.uc >= 25 ? 3 : b.uc >= 10 ? 2 : 1);
  return b;
});
writeFileSync(join(KOK, "bulgu.json"), JSON.stringify(BULGU));

// Uc siralama. Esitlik bozucu bulgu numarasidir: siralama kosumdan kosuma ayni kalir.
const sirala = (alan) => [...BULGU].sort((a, b) => b[alan] - a[alan] || a.no - b.no);
const OLCUT = [["ciddiyet", "ciddiyet"], ["somurulebilirlik", "somuru"], ["maruziyet", "maruziyet"]];
const SIRA = Object.fromEntries(OLCUT.map(([ad, alan]) => [ad, sirala(alan)]));

const N = 10;
const S = [6, 22, 22, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["sira", "ciddiyet", "somurulebilirlik", "maruziyet"]);
const yaz = (b, alan) => `#${String(b.no).padStart(2, "0")} ${b.kaynak} ${b[alan]}`;
for (let i = 0; i < N; i++)
  satir([i + 1, yaz(SIRA.ciddiyet[i], "ciddiyet"), yaz(SIRA.somurulebilirlik[i], "somuru"),
    yaz(SIRA.maruziyet[i], "maruziyet")]);

const ilkOn = Object.fromEntries(OLCUT.map(([ad]) => [ad, new Set(SIRA[ad].slice(0, N).map((b) => b.no))]));
const kume = Object.values(ilkOn);
const kacinda = (no) => kume.filter((k) => k.has(no)).length;
const birlesim = [...new Set(kume.flatMap((k) => [...k]))];
console.log("");
console.log(`bulgu ${BULGU.length}; ilk ${N} listelerinin birlesimi ${birlesim.length} kalem, ` +
  `kesisimi ${birlesim.filter((no) => kacinda(no) === 3).length} kalem; tohum 330941`);
console.log(`ucunde de ilk ${N}: ${birlesim.filter((no) => kacinda(no) === 3).length}, ` +
  `ikisinde: ${birlesim.filter((no) => kacinda(no) === 2).length}, ` +
  `yalniz birinde: ${birlesim.filter((no) => kacinda(no) === 1).length}`);
console.log(`ucunde de ilk ${N}'da olan tek kalem: ` +
  birlesim.filter((no) => kacinda(no) === 3).map((no) => `#${no}`).join(", ") +
  `; hicbir olcutte ilk ${N}'a girmeyen bulgu ${BULGU.length - birlesim.length}`);
```

```
sira  ciddiyet              somurulebilirlik      maruziyet
1     #06 koken 10          #04 bagimlilik 10     #09 koken 10
2     #14 bagimlilik 10     #06 koken 10          #21 calisan uyg 10
3     #20 statik 10         #15 koken 10          #25 koken 10
4     #25 koken 10          #22 imaj katmani 10   #27 bagimlilik 10
5     #27 bagimlilik 10     #24 sir tarama 10     #31 sablon 10
6     #34 sablon 10         #25 koken 10          #05 politika 9
7     #07 politika 9        #03 sablon 8          #16 koken 9
8     #08 koken 9           #09 koken 8           #18 calisan uyg 9
9     #04 bagimlilik 8      #13 politika 8        #22 imaj katmani 9
10    #05 politika 8        #16 koken 8           #29 koken 9

bulgu 34; ilk 10 listelerinin birlesimi 21 kalem, kesisimi 1 kalem; tohum 330941
ucunde de ilk 10: 1, ikisinde: 7, yalniz birinde: 13
ucunde de ilk 10'da olan tek kalem: #25; hicbir olcutte ilk 10'a girmeyen bulgu 13
```

Üç sütun aynı kalemlerin üç görüntüsüdür ve **üçünde de ilk ona giren tek bir kalem
vardır: #25.** Üç listenin birleşimi 21 kalemdir — "ilk on" demek, yirmi bir kalemden onunu
seçmek demektir.

Birleşimin **13 kalemi yalnız bir ölçütte** ilk ona girer; ölçüt seçimi o kalemleri kuyruğun
başına da sonuna da koyabilir. Kuyruğun sessiz kısmı da tabloda: otuz dört bulgunun **13'ü
hiçbir ölçütte ilk ona girmez.**

#### Sıralama Kaç Yerde Değişiyor

İlk on karşılaştırması yalnız listenin tepesini görür. Asıl soru, iki sıralamanın **kaç çiftte**
birbirine ters düştüğüdür.

```js
// olcum-agi/kayma.mjs — uc siralama arasinda kac yerde sira degisiyor
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const BULGU = JSON.parse(readFileSync(join(KOK, "bulgu.json")));

const OLCUT = [["ciddiyet", "ciddiyet"], ["somurulebilirlik", "somuru"], ["maruziyet", "maruziyet"]];
const sirala = (alan) => [...BULGU].sort((a, b) => b[alan] - a[alan] || a.no - b.no);
// Konum: kalemin o siralamadaki yeri (1 en ustte). Esitlik bozucu bulgu numarasidir.
const KONUM = Object.fromEntries(OLCUT.map(([ad, alan]) =>
  [ad, new Map(sirala(alan).map((b, i) => [b.no, i + 1]))]));
const ilkOn = Object.fromEntries(OLCUT.map(([ad]) =>
  [ad, new Set(sirala(OLCUT.find(([a]) => a === ad)[1]).slice(0, 10).map((b) => b.no))]));

const CIFT = [["ciddiyet", "somurulebilirlik"], ["ciddiyet", "maruziyet"],
  ["somurulebilirlik", "maruziyet"]];
const S = [30, 14, 9, 13, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["karsilastirma", "ters donen", "oran", "ilk 10 ortak", "en buyuk kayma"]);
const toplamCift = (BULGU.length * (BULGU.length - 1)) / 2;
for (const [x, y] of CIFT) {
  let ters = 0, enBuyuk = 0;
  for (let i = 0; i < BULGU.length; i++)
    for (let j = i + 1; j < BULGU.length; j++) {
      const [a, b] = [BULGU[i].no, BULGU[j].no];
      const solda = KONUM[x].get(a) < KONUM[x].get(b);
      if (solda !== (KONUM[y].get(a) < KONUM[y].get(b))) ters++;
    }
  for (const b of BULGU)
    enBuyuk = Math.max(enBuyuk, Math.abs(KONUM[x].get(b.no) - KONUM[y].get(b.no)));
  const ortak = [...ilkOn[x]].filter((no) => ilkOn[y].has(no)).length;
  satir([`${x} - ${y}`, `${ters}/${toplamCift}`, `%${Math.round((100 * ters) / toplamCift)}`,
    `${ortak}/10`, `${enBuyuk} sira`]);
}

// En cok kayan bes kalem: uc siradaki en yuksek ve en dusuk konumun farki.
const yayilim = (b) => {
  const k = OLCUT.map(([ad]) => KONUM[ad].get(b.no));
  return { b, k, fark: Math.max(...k) - Math.min(...k) };
};
const T = [8, 16, 12, 12, 0];
const satir2 = (s) => console.log(s.map((v, i) => String(v).padEnd(T[i])).join(""));
console.log("");
satir2(["bulgu", "kaynak", "ciddiyet", "somuru", "maruziyet"]);
for (const { b, k } of BULGU.map(yayilim).sort((x, y) => y.fark - x.fark || x.b.no - y.b.no).slice(0, 5))
  satir2([`#${b.no}`, b.kaynak, `${b.ciddiyet} (${k[0]}.)`, `${b.somuru} (${k[1]}.)`,
    `${b.maruziyet} (${k[2]}.)`]);
console.log("");
const enYayik = BULGU.map(yayilim).sort((x, y) => y.fark - x.fark)[0];
console.log(`otuz dort kalemin ${BULGU.filter((b) => yayilim(b).fark >= 15).length} tanesi uc ` +
  `siralamada 15 siradan fazla yer degistiriyor; en yayigi #${enYayik.b.no} ile ${enYayik.fark} sira`);
```

```
karsilastirma                 ters donen    oran     ilk 10 ortak en buyuk kayma
ciddiyet - somurulebilirlik   295/561       %53      3/10         31 sira
ciddiyet - maruziyet          290/561       %52      3/10         29 sira
somurulebilirlik - maruziyet  277/561       %49      4/10         29 sira

bulgu   kaynak          ciddiyet    somuru      maruziyet
#14     bagimlilik      10 (2.)     0 (33.)     7 (25.)
#15     koken           5 (26.)     10 (3.)     3 (32.)
#22     imaj katmani    3 (33.)     10 (4.)     9 (9.)
#31     sablon          3 (34.)     6 (24.)     10 (5.)
#9      koken           4 (29.)     8 (8.)      10 (1.)

otuz dort kalemin 21 tanesi uc siralamada 15 siradan fazla yer degistiriyor; en yayigi #14 ile 31 sira
```

Ters dönen çift oranı üç karşılaştırmada da **%49 ile %53 arasındadır**: 561 çiftin yaklaşık
yarısında iki ölçüt birbirine ters düşer. **Ciddiyetle sıralanmış bir liste, sömürülebilirlikle
sıralanmış listenin yaklaşık yarısını ters çevirir.** Ölçütler birbirinin yaklaşımı değildir; ayrı
şeyler ölçerler.

İkinci tablo bunu kalemlerde gösterir. **#14 ciddiyette ikinci, sömürülebilirlikte otuz
üçüncüdür** — ağırlığı en yüksek kalemlerden biridir ve ona giden çalışan bir yol yoktur. Karşıt
uç **#31**'dir: ciddiyette otuz dördüncü, maruziyette beşinci. Kalemlerin **21'i** on beş sıradan
fazla yer değiştirir.

#### Sonlu Kapasite

Sıralama tek başına sonuç değildir; sonucu doğuran şey listenin nerede kesildiğidir.

**TZ27.** Birleşik sıra üç sıradaki konumların toplamıdır; ağırlık yoktur.
**TZ28.** Kapasite on iki kalemdir; kalem başına 45 dakika düzeltme veridir, ölçülmüş değildir.

```js
// olcum-agi/kapasite.mjs — sonlu kuyruk: hangi siralama ilk on ikiye neyi aliyor
import { readFileSync } from "node:fs";
import { join } from "node:path";

const KOK = join(process.env.TMPDIR ?? "/tmp", "olcum-agi-tz");
const BULGU = JSON.parse(readFileSync(join(KOK, "bulgu.json")));

const OLCUT = [["ciddiyet", "ciddiyet"], ["somurulebilirlik", "somuru"], ["maruziyet", "maruziyet"]];
const sirala = (alan) => [...BULGU].sort((a, b) => b[alan] - a[alan] || a.no - b.no);
const KONUM = Object.fromEntries(OLCUT.map(([ad, alan]) =>
  [ad, new Map(sirala(alan).map((b, i) => [b.no, i + 1]))]));
// Birlesik sira: uc siralamadaki konumlarin toplami. Agirlik yok, uc olcut esit sayilir.
const toplamKonum = (b) => OLCUT.reduce((n, [ad]) => n + KONUM[ad].get(b.no), 0);
const birlesik = [...BULGU].sort((a, b) => toplamKonum(a) - toplamKonum(b) || a.no - b.no);

const KAPASITE = 12, DUZELTME_DK = 45;                     // sure VERI olarak verilmistir
const LISTE = [...OLCUT.map(([ad, alan]) => [ad, sirala(alan)]), ["birlesik", birlesik]]
  .map(([ad, dizi]) => [ad, dizi.slice(0, KAPASITE)]);
const ort = (kume, alan) => (kume.reduce((n, b) => n + b[alan], 0) / kume.length).toFixed(1);

const S = [20, 14, 13, 15, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(S[i])).join(""));
satir(["siralama", "ort. ciddiyet", "ort. somuru", "ort. maruziyet", "birlesikle ortak"]);
const birlesikNo = new Set(LISTE.at(-1)[1].map((b) => b.no));
for (const [ad, kume] of LISTE)
  satir([ad, ort(kume, "ciddiyet"), ort(kume, "somuru"), ort(kume, "maruziyet"),
    `${kume.filter((b) => birlesikNo.has(b.no)).length}/${KAPASITE}`]);

const hicbirinde = BULGU.filter((b) => LISTE.every(([, k]) => k.every((x) => x.no !== b.no)));
const somurusuz = LISTE[0][1].filter((b) => b.somuru <= 2).length;
const disarida = sirala("somuru").slice(0, KAPASITE).filter((b) => !birlesikNo.has(b.no)).length;
console.log("");
console.log(`kapasite ${KAPASITE} kalem, kalem basina ${DUZELTME_DK} dk duzeltme (veri): ` +
  `${KAPASITE * DUZELTME_DK} dk; kuyrukta kalan ${BULGU.length - KAPASITE} kalem`);
console.log(`yalniz ciddiyetle siralanan ilk ${KAPASITE}'nin ${somurusuz} kaleminde calisan bir ` +
  `yol yok; somurulebilirlik sirasinin ${disarida} kalemi birlesik sirada disarida kaliyor`);
console.log(`hicbir siralamanin ilk ${KAPASITE}'sine girmeyen bulgu ${hicbirinde.length}/` +
  `${BULGU.length}; ${hicbirinde.filter((b) => b.uretimde).length} tanesi uretimde duruyor`);
```

```
siralama            ort. ciddiyet ort. somuru  ort. maruziyet birlesikle ortak
ciddiyet            9.2           6.0          7.3            7/12
somurulebilirlik    6.8           9.0          7.5            8/12
maruziyet           6.1           6.3          9.3            7/12
birlesik            8.0           7.5          8.8            12/12

kapasite 12 kalem, kalem basina 45 dk duzeltme (veri): 540 dk; kuyrukta kalan 22 kalem
yalniz ciddiyetle siralanan ilk 12'nin 2 kaleminde calisan bir yol yok; somurulebilirlik sirasinin 4 kalemi birlesik sirada disarida kaliyor
hicbir siralamanin ilk 12'sine girmeyen bulgu 9/34; 5 tanesi uretimde duruyor
```

Her sıralama kendi ölçütünde en yükseği seçer, ötekilerde ortalamaya düşer: ciddiyet sırası 9,2
ortalama ciddiyet getirir, sömürülebilirlik ortalaması 6,0'da kalır. Birleşik sıra hiçbir sütunda
en yüksek değildir, üçünde de sekizin üstündedir. **Üç ölçütü birleştirmek en iyi listeyi değil,
en az kötü listeyi verir.**

Yalnız ciddiyetle sıralanan ilk on ikinin **2 kaleminde çalışan bir yol yoktur**: doksan dakika
harcanır, kapanan yol sıfırdır. Son satır kuyruğun kör noktasını yazar:
**9 bulgu hiçbir sıralamanın ilk on ikisine girmez ve 5'i üretimde durur.**

#### Özet

- Aynı 34 bulgu üç ölçütle sıralandığında üçünde de ilk ona giren tek kalem vardır; birleşim 21
  kalem, 13'ü yalnız bir ölçütte.
- Ters dönen çift oranı %49 ile %53 arasındadır; 34 kalemin 21'i on beşten fazla sıra kayar.
  Ciddiyette ikinci olan kalem sömürülebilirlikte otuz üçüncüdür.
- Birleşik sıra hiçbir sütunda en yüksek değildir ama üçünde de sekizin üstündedir; yalnız
  ciddiyetle seçilen ilk on ikinin 2 kaleminde çalışan bir yol yoktur.
- Kapasite 12 kalem ve 540 dakikadır; 22 kalem kuyrukta kalır, 9 bulgu hiçbir sıralamanın ilk
  on ikisine girmez ve 5'i üretimde durur.

#### Kurs Kapanışı

| Ders | Taranan / bulgu | Gerçek / yanlış pozitif / görülmeyen | Hattan aldığı süre |
|---|---|---|---|
| Statik uygulama güvenlik testi | 720 nokta / 418 bulgu | 101 / 317 / 62 | 2 085 dk, 61 yanlış bloke |
| Dinamik uygulama güvenlik testi | 84 uç / 17 bulgu | 10 / 7 / 18 | 5 316 dk, gecelik 443 dk |
| Bağımlılık bileşen analizi | 210 bileşen / 139 eşleşme | 31 / 108 / 12 | 1 870 dk, 57 bloke yapı |
| Depo geçmişinde sır tarama | 1 800 işleme / 677 eşleşme | 260 / 417 / 50 | 57 dk tarama, 1 668 dk inceleme |
| Konteyner imaj tarama | 348 bileşen / 144 bulgu | 144 / ölçülmedi / 18 | 1 805 dk, 49 bloke yapı |
| Kod olarak altyapı taraması | 420 kalem / 55 kesin bulgu | 55 / 0 / 19 | 800 dk, 34 bloke yapı |
| Yazılım malzeme listesi | 73 kayıt / 12 soru | 8 cevaplanan / 3 eksik alanla / 4 sorulamayan | 820 s, seyrekte 41 s |
| Yapı çıktısı imzalama | 124 çıktı / 8 iddia | 3 kanıtlanan / 5 geçen yanlış / 4 sahte imza | 120 çıktı yeniden üretim |
| Köken bilgisi | 14 alan / 12 yapı | 4 kanıt alanı / 3 yanlış işaret / 7 sınanamaz beyan | 480 s, yeniden üretimle 9 040 s |
| Derleme ortamı yalıtımı | 18 kalem / 90 erişim çifti | 15 gerekli / 75 gereksiz / 25 kapatılamayan | 1 380 s, 30 koşum |
| Bağımlılık politikası | 48 bileşen / 46 işaret | 10 gerçek / 20 yanlış işaret / 3 görülmeyen | 270 s, 96 dk müdahale |
| Bulgu önceliklendirme | 34 bulgu / 3 sıralama | 1 üçünde de üstte / 13 yalnız birinde / 9 hiçbir listede | 540 dk, 22 kalem kuyrukta |

Üçüncü sütun on iki satırda da doludur; kursun kuralı odur: **otomatik denetimin değeri bulduğu
bulgu sayısı değil, kaçırdığını bilmen ve hattan aldığı sürenin karşılığını almandır.** Kaçırdığı
yazılmayan tarama bir güvence değil, bir yanılsamadır. Erişim denetimi açıkları iki denetimde de
sıfır bulguyla kapandı, 50 sır hiçbir desene takılmadı, 19 ihlal için kural yoktu ve bakım devri
hiçbir alanda görünmedi; bu sayılar raporlanmadıkça "sıfır bulgu" ile "hiç bakılmadı" ayırt
edilemez.

Dördüncü sütun aynı sertlikte okunur: hattan alınan sürenin karşılığı her zaman alınmaz. Köken
belgesinin varlığını aramak 480 saniye harcayıp dört bozuk isteğin dördünü de geçirdi. Bir
denetimin ölçülmüş sayılması için üç sütunun üçünün de yazılması gerekir.

Kod tarandı, bileşenler envanterlendi ve yapı çıktısı imzalandı. Bütün bu denetimlerin ortak bir
sınırı var: hepsi **dağıtımdan önce** duruyordu. Yirmi iki bulgu kuyrukta kaldı, dokuzu hiçbir
listeye girmedi ve onlar üretime kendi hâlleriyle gidiyor. **M25/K04 Çalışma Zamanı Güvenliği ve
Sır Yönetimi** o andan sonrasını sorar: uygulama çalışmaya başladığı andan itibaren onu neyin
koruduğu bu kursta hiç sorulmadı.

