---
title: 'Statik Çözümleme ve Güvenlik Denetimi'
source: 'https://academia.sh/tr/kurslar/go-uretim/statik-cozumleme-ve-guvenlik-denetimi'
course: 'Üretim Go: Test, Araçlar ve Başarım'
language: tr
updated: '2026-09-13T05:08:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Aynı iki kaynak arasındaki farkı, derleyicinin göremediği ama bir çözümleyicinin çıkış koduyla bildirdiği bir ölçümle ayırt eder'
    - 'Derleyicinin hiçbirini reddetmediği üç kalıptan kaçının çözümleyici tarafından yakalandığını sayar'
    - 'Çözümlemenin temiz çıkmasının kusursuz olmak anlamına gelmediğini, yakalanmayan bir kalıbı koşturarak sınırlar'
---

# Statik Çözümleme ve Güvenlik Denetimi

Aynı iki kaynaktan biri temiz, öteki kusurlu; derleyicinin ikisini de aynı şekilde kabul ettiği, ama bir çözümleyicinin aralarındaki farkı çıkış koduyla bildirdiği ölçülür. Üç kalıptan derleyicinin hiçbirini yakalamadığı, çözümleyicinin ikisini yakaladığı sayılıyor. Sınırlayıcı ölçüm, çözümlemenin temiz çıkmasının kusursuz olmak anlamına gelmediğini — yakalanmayan üçüncü kalıbın koşum sırasında gerçekten çöktüğünü — koşturarak gösteriyor. Son ölçüm, bir bağımlılığın bildirilen sürümünü modellenmiş bir eşik listesiyle karşılaştırıyor.

Önceki derste ölçülen araç, kaynağı yalnızca isimler ve tipler düzeyinde okudu; gövdelerin
içinde ne olduğuna hiç bakmadı. Bu ders tam o boşluğa iniyor: gövdenin kendisini okuyan, belirli
kalıpları arayan bir araç. Ama bu aracın da kendi sınırı var — bulduğunu söylemesi, aramadığı
bir şeyin de yok olduğu anlamına gelmiyor.

## İki Kaynak, Bir Çözümleyici

**Statik çözümleme (static analysis)**, bir kaynağı hiç çalıştırmadan, yalnızca metnini
okuyarak belirli hata kalıplarını arayan bir işlemdir. Dilin kendi dağıtımının çözümleme aracı
bu işi tek bir komutla yapıyor ve yalnızca **çıkış kodu** bildiriyor — bulduğu satırların
metnini bu derste hiç okumuyoruz, yalnızca temiz mi kusurlu mu olduğunu.

Aşağıdaki ölçüm iki kaynak kuruyor: biri bir tam sayıyı doğru biçimlendiriciyle yazdırıyor,
öteki aynı biçimlendiriciye bir dizgi veriyor — tip ile biçim arasında bir uyuşmazlık.

```go
// yardimcilar.go — bu dersin paylastigi yardimcilar: gecici paket kurma ve arac cagirma
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

// goster bir bilgiyi, kimin soyledigini ve kanitini tek satirda yazdirir.
func goster(bilgi, soyleyen, kanit string) {
	fmt.Printf("%-40s %-7s %s\n", bilgi, soyleyen, kanit)
}

// paket, verilen dosyalari yeni bir gecici modulun icine yazar; yol hicbir yerde basilmaz.
func paket(dosyalar map[string]string) string {
	dizin, err := os.MkdirTemp("", "cozumleme")
	if err != nil {
		return ""
	}
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module cozumlemedeneme\n\ngo 1.24\n"), 0o644)
	for ad, icerik := range dosyalar {
		os.WriteFile(filepath.Join(dizin, ad), []byte(icerik), 0o644)
	}
	return dizin
}

// aracCalistir dilin kendi komutunu kosturur, ciktiyi ve basari durumunu doner; cikti basilmaz.
func aracCalistir(dizin string, arg ...string) (string, bool) {
	cmd := exec.Command("go", arg...)
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	cikti, err := cmd.CombinedOutput()
	return string(cikti), err == nil
}

// derlenebilirMi, verilen dizindeki paketi derlemeyi dener.
func derlenebilirMi(dizin string) bool {
	_, tamam := aracCalistir(dizin, "build", "-o", os.DevNull, ".")
	return tamam
}

// temizMi, verilen dizindeki paketin statik cozumlemeden temiz gecip gecmedigini soyler.
func temizMi(dizin string) bool {
	_, tamam := aracCalistir(dizin, "vet", "./...")
	return tamam
}
```

```go
// olcum1.go — ayni iki kaynak, biri temiz biri kusurlu, tek bir cozumleyiciden geciyor
package main

import "fmt"

func main() {
	temiz := paket(map[string]string{
		"prog.go": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Printf(\"deger=%d\\n\", 7)\n}\n",
	})
	kusurlu := paket(map[string]string{
		"prog.go": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Printf(\"deger=%d\\n\", \"metin\")\n}\n",
	})

	fmt.Println("-- iki kaynak, bir cozumleyici --")
	goster("temiz kaynak derlendi mi", "arac", fmt.Sprint(derlenebilirMi(temiz)))
	goster("kusurlu kaynak derlendi mi", "arac", fmt.Sprint(derlenebilirMi(kusurlu)))
	goster("temiz kaynak cozumlemesi", "arac", fmt.Sprint("temiz=", temizMi(temiz)))
	goster("kusurlu kaynak cozumlemesi", "arac", fmt.Sprint("temiz=", temizMi(kusurlu)))
}
```

```
-- iki kaynak, bir cozumleyici --
temiz kaynak derlendi mi                 arac    true
kusurlu kaynak derlendi mi               arac    true
temiz kaynak cozumlemesi                 arac    temiz=true
kusurlu kaynak cozumlemesi               arac    temiz=false
```

**DR15.** İki kaynak da derleniyor — tip uyuşmazlığı derleyici düzeyinde bir hata değil,
çünkü biçimlendirme dizgisi ile argümanlar arasındaki ilişki, derleyicinin tip denetiminin
konusu değil. Çözümleyici ise bu iki satırı ayırt ediyor: birinde biçimlendirici ile argüman
tipi eşleşiyor, ötekinde eşleşmiyor. Bu, önceki derslerdeki "araç" bilgisinin bir başka
biçimi — çözümleyici kaynağı çalıştırmadan, yalnızca okuyarak bir kalıp arıyor ve bulduğunu
tek bir çıkış koduyla bildiriyor.

## Derleyicinin Yakalamadığı Kalıp

Yukarıdaki tek kalıp bir başlangıç. Aşağıdaki ölçüm üç ayrı kalıbı bir arada deniyor: bir
biçim uyuşmazlığı, bir kilit değerinin kopyalanması ve bir hatanın yok sayılması. Üçü de
derleniyor; sorulan soru, kaçının çözümleyici tarafından yakalandığı.

```go
// olcum2.go — derleyicinin hicbirini reddetmedigi uc kalip, cozumleyiciden gecerken ayriliyor
package main

import "fmt"

const kalipBicim = "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Printf(\"deger=%d\\n\", \"metin\")\n}\n"

const kalipKilit = "package main\n\nimport \"sync\"\n\ntype Sayac struct {\n\tkilit sync.Mutex\n\tdeger int\n}\n\nfunc kopyala(s Sayac) Sayac {\n\treturn s\n}\n\nfunc main() {\n\tkopyala(Sayac{})\n}\n"

const kalipYokSayilanHata = "package main\n\nimport \"fmt\"\n\ntype Kayit struct{ Ad string }\n\nfunc bul(anahtar string) (*Kayit, error) {\n\tif anahtar == \"\" {\n\t\treturn nil, fmt.Errorf(\"anahtar bos\")\n\t}\n\treturn &Kayit{Ad: anahtar}, nil\n}\n\nfunc main() {\n\tk, _ := bul(\"\")\n\tfmt.Println(k.Ad)\n}\n"

func main() {
	kaliplar := []struct{ ad, kaynak string }{
		{"bicim uyusmazligi", kalipBicim},
		{"kilit kopyalama", kalipKilit},
		{"yok sayilan hata", kalipYokSayilanHata},
	}

	fmt.Println("-- derleyicinin yakalamadigi uc kalip --")
	derlenenSayisi, temizSayisi := 0, 0
	for _, k := range kaliplar {
		dizin := paket(map[string]string{"prog.go": k.kaynak})
		d := derlenebilirMi(dizin)
		t := temizMi(dizin)
		if d {
			derlenenSayisi++
		}
		if t {
			temizSayisi++
		}
		goster(k.ad+" derlendi mi", "arac", fmt.Sprint(d))
		goster(k.ad+" cozumleme temiz mi", "arac", fmt.Sprint(t))
	}
	goster("uc kaliptan derlenen sayisi", "arac", fmt.Sprint(derlenenSayisi))
	goster("uc kaliptan cozumlemeyi gecen sayisi", "arac", fmt.Sprint(temizSayisi))
	goster("cozumleyicinin yakaladigi kalip sayisi", "arac", fmt.Sprint(len(kaliplar)-temizSayisi))
}
```

```
-- derleyicinin yakalamadigi uc kalip --
bicim uyusmazligi derlendi mi            arac    true
bicim uyusmazligi cozumleme temiz mi     arac    false
kilit kopyalama derlendi mi              arac    true
kilit kopyalama cozumleme temiz mi       arac    false
yok sayilan hata derlendi mi             arac    true
yok sayilan hata cozumleme temiz mi      arac    true
uc kaliptan derlenen sayisi              arac    3
uc kaliptan cozumlemeyi gecen sayisi     arac    1
cozumleyicinin yakaladigi kalip sayisi   arac    2
```

**DR16.** Üç kalıbın üçü de derleniyor — derleyicinin yakaladığı kalıp sayısı sıfır. Bu üç
kalıp derleyicinin çalıştığı katmanın tümüyle dışında duruyor, çünkü üçü de **tipçe geçerli**
kod: yanlış biçimlendirilmiş bir yazdırma çağrısı, bir alanı kopyalanan bir yapı, kullanılmayan
bir hata değeri — bunların hiçbiri bir tip uyuşmazlığı ya da bir söz dizimi hatası değil.
Çözümleyici üç kalıptan ikisini yakalıyor: biçim uyuşmazlığını ve kilit kopyalamasını. Üçüncüsü
— hatanın yok sayılması — çözümlemeden **temiz** çıkıyor. Bu üçüncü kalıp bir sonraki bölümün
konusu.

Yakalanan iki kalıbın ortak bir özelliği var: ikisi de dilin kendi tip sisteminin **dışında**
kalan, ama yine de mekanik olarak tanınabilen bir örüntüye uyuyor. Biçim uyuşmazlığında
örüntü, bir biçimlendirme dizgisindeki her `%` işaretinin, verilen argümanların tipiyle
eşleşip eşleşmediği; kilit kopyalamasında örüntü, `sync` paketinin belgelediği bir tipin
(kilit içeren herhangi bir yapı) değerle değil yalnızca işaretçiyle taşınması gerektiği.
Çözümleyici bu iki kuralı, kaynağı ayrıştırıp söz dizim ağacında belirli bir biçimi arayarak
uyguluyor — tıpkı önceki dersteki imza karşılaştırıcısının yaptığı gibi, yalnızca aranan
biçim burada bir imza değil, bir kullanım kalıbı. Üçüncü kalıbın yakalanmaması, çözümleyicinin
bu iki kuraldan **hiçbirine** uymadığı için değil, hatanın yok sayılmasını arayan bir kural
listede hiç olmadığı için.

## Sınırlayıcı Ölçüm: Temiz Çıkmak Kusursuz Demek Değil

Yukarıdaki üçüncü kalıp, bir işlevin döndürdüğü hatayı yok sayıp sonucu doğrudan kullanan bir
çağrı. Çözümleyici bunu temiz buldu; derleyici de reddetmedi. Aşağıdaki ölçüm bu kalıbı
gerçekten koşturuyor — bu kez bir alt süreç çağırarak değil, aynı mantığı bu ölçümün kendi
içinde çalıştırarak, çünkü panik burada `recover` ile yakalanıp yalnızca bir doğruluk değeri
olarak okunacak.

```go
// olcum3.go — sinirlayici olcum: temiz cikmak kusursuz demek degil
package main

import "fmt"

const kalipYokSayilanHata = "package main\n\nimport \"fmt\"\n\ntype Kayit struct{ Ad string }\n\nfunc bul(anahtar string) (*Kayit, error) {\n\tif anahtar == \"\" {\n\t\treturn nil, fmt.Errorf(\"anahtar bos\")\n\t}\n\treturn &Kayit{Ad: anahtar}, nil\n}\n\nfunc main() {\n\tk, _ := bul(\"\")\n\tfmt.Println(k.Ad)\n}\n"

// Kayit ve bul, yukaridaki kaynagin gercek, calistirilabilir bir eslesi: cozumleyicinin
// okudugu metinle birebir ayni mantigi tasiyor.
type Kayit struct{ Ad string }

func bul(anahtar string) (*Kayit, error) {
	if anahtar == "" {
		return nil, fmt.Errorf("anahtar bos")
	}
	return &Kayit{Ad: anahtar}, nil
}

// dene, hatayi yok sayan cagriyi kosturur ve panigi yakalar; yigit izi hicbir yerde basilmaz.
func dene() (panikledi bool) {
	defer func() {
		if r := recover(); r != nil {
			panikledi = true
		}
	}()
	k, _ := bul("")
	_ = k.Ad
	return false
}

func main() {
	dizin := paket(map[string]string{"prog.go": kalipYokSayilanHata})

	fmt.Println("-- sinirlayici olcum: temiz cikmak kusursuz demek degil --")
	goster("cozumleme sonucu", "arac", fmt.Sprint("temiz=", temizMi(dizin)))
	goster("calistirildiginda panikledi mi", "kosum", fmt.Sprint(dene()))
}
```

```
-- sinirlayici olcum: temiz cikmak kusursuz demek degil --
cozumleme sonucu                         arac    temiz=true
calistirildiginda panikledi mi           kosum   true
```

**DR17.** Çözümleyicinin verdiği yanıt (`temiz=true`) ve koşumun verdiği yanıt (`panikledi=true`)
aynı kaynak hakkında iki ayrı şey söylüyor ve ikisi de doğru. Çözümleyici "aradığım kalıplardan
hiçbirini bulamadım" diyor — bu, bir hatanın yok sayılmasını arayan bir kural içermiyor. Koşum
ise "bu kaynak, bu girdiyle çalıştırıldığında çöküyor" diyor. Bu iki yanıt çelişmiyor; ikisi de
aracın **kapsamıyla** sınırlı. `temiz=true` satırı bu yüzden "bu kaynakta kusur yok" diye
okunamaz — yalnızca "çözümleyicinin bildiği kalıplardan hiçbiri bu kaynakta yok" diye okunabilir.
Bu, kursun daha önce kapsama oranı için kurduğu ayrımın aynısı: bir aracın sessiz kalması, o
aracın **aramadığı** şeyin var olmadığı anlamına gelmiyor.

Bu sınırın pratikteki karşılığı, bir çözümleyicinin kural listesinin **sabit** olmadığı ama
her zaman **sonlu** olduğu. Yeni bir kalıp fark edildikçe listeye yeni bir kural eklenebiliyor
— bu ders özelinde kullanılan iki kural (biçim uyuşmazlığı, kilit kopyalama) da böyle
eklenmiş, gerçek bir hatanın gerçek programlarda tekrar tekrar görülmesinden sonra listeye
girmiş kurallar. Ama liste ne kadar büyürse büyüsün, her zaman henüz eklenmemiş bir kalıp
kalıyor; bu ders bu boşluğu somutlaştırmak için tek bir örnek seçti, ama seçilen örneğin
kendisi önemli değil — önemli olan, çözümlemenin **temiz** yanıtının her zaman "şu ana kadar
bilinen kurallara göre temiz" anlamına geldiği, mutlak bir doğruluk iddiası taşımadığı.

## Bilinen Açık Taraması: Modellenen Bir Liste

Çözümleyicinin aradığı kalıplar dilin kendi kurallarıyla sınırlı. Bir bağımlılığın taşıdığı
bilinen bir **açık (vulnerability)** ise ayrı bir bilgi türü — kodun kendi biçiminden değil,
o bağımlılığın **hangi sürümde** düzeltildiğinden geliyor. **Bilinen açık taraması (known
vulnerability scan)**, bir bağımlılığın bildirilen sürümünü, düzeltmenin geldiği sürümle
karşılaştırıyor. Aşağıdaki ölçüm gerçek bir açık listesine hiç bağlanmıyor; karşılaştırma
kaynak içinde, küçük bir eşik tablosuyla **modelleniyor**.

```go
// olcum4.go — bilinen acik taramasi, modellenen bir esik listesiyle kurulan bir arac
package main

import "fmt"

// Surum uc parcali bir surum numarasini tasir (M09/K01 go-packages/02'deki kalibin aynisi).
type Surum struct{ Ana, Ikincil, Yama int }

func (s Surum) metin() string { return fmt.Sprintf("%d.%d.%d", s.Ana, s.Ikincil, s.Yama) }

func (s Surum) kucukMu(diger Surum) bool {
	if s.Ana != diger.Ana {
		return s.Ana < diger.Ana
	}
	if s.Ikincil != diger.Ikincil {
		return s.Ikincil < diger.Ikincil
	}
	return s.Yama < diger.Yama
}

// guvenliEsik, her modul icin bilinen en dusuk guvenli surumu tutan MODELLENMIS bir tablo;
// gercek bir aciklar veritabani degil, yalnizca bu olcumun kendi kurdugu kucuk bir liste.
var guvenliEsik = map[string]Surum{
	"kutuphaneX": {1, 4, 0},
	"kutuphaneY": {2, 0, 0},
}

type Bagimlilik struct {
	Modul      string
	Bildirilen Surum
}

// acikVarMi, bildirilen surumun modellenen esikten dusuk olup olmadigina bakar.
func acikVarMi(b Bagimlilik) bool {
	esik, varMi := guvenliEsik[b.Modul]
	if !varMi {
		return false
	}
	return b.Bildirilen.kucukMu(esik)
}

func main() {
	bagimliliklar := []Bagimlilik{
		{Modul: "kutuphaneX", Bildirilen: Surum{1, 2, 0}},
		{Modul: "kutuphaneY", Bildirilen: Surum{2, 3, 0}},
	}

	fmt.Println("-- bilinen acik taramasi: modellenen bir liste --")
	acikSayisi := 0
	for _, b := range bagimliliklar {
		a := acikVarMi(b)
		if a {
			acikSayisi++
		}
		goster(b.Modul+" bildirilen="+b.Bildirilen.metin(), "arac", fmt.Sprint("acik=", a))
	}
	goster("iki bagimliliktan acik bulunan sayisi", "arac", fmt.Sprint(acikSayisi))
}
```

```
-- bilinen acik taramasi: modellenen bir liste --
kutuphaneX bildirilen=1.2.0              arac    acik=true
kutuphaneY bildirilen=2.3.0              arac    acik=false
iki bagimliliktan acik bulunan sayisi    arac    1
```

**DR18.** `kutuphaneX` için bildirilen sürüm (`1.2.0`), modellenen eşiğin (`1.4.0`) altında
kalıyor — açık var. `kutuphaneY` için bildirilen sürüm (`2.3.0`) eşiği (`2.0.0`) aşıyor — açık
yok. Bu karşılaştırma da önceki iki ölçümle aynı sınırı taşıyor: eşik tablosunun kendisi
**bu ölçümün kurduğu bir modeldir**, gerçek bir kayıttan gelmiyor ve bir modül tabloda hiç
yoksa `acikVarMi` sessizce `false` dönüyor — tablo eksikse tarama hiçbir şey bulmuyor, tıpkı
çözümleyicinin aramadığı kalıbı hiç görmediği gibi. **M09/K01 Go Temelleri**'nin en düşük
sürüm seçimi ölçümü hangi sürümün seçildiğini kurmuştu; bu ölçüm o seçilen sürümün bilinen bir
eşiğin altında kalıp kalmadığını, ayrı ve modellenmiş bir karşılaştırmayla soruyor.

## Özet

- Statik çözümleme kaynağı hiç çalıştırmadan, belirli kalıpları arayarak çalışır; derleyicinin
  kabul ettiği iki kaynak arasındaki farkı yalnızca çıkış koduyla bildirir.
- Derleyicinin hiçbirini reddetmediği üç kalıptan ikisi çözümleyici tarafından yakalandı; biri
  — hatanın yok sayılması — temiz çıktı.
- Sınırlayıcı ölçüm: temiz çıkan kalıp, gerçekten koşturulduğunda çöktü; çözümlemenin
  sessizliği kusursuzluk değil, aranan kalıbın bulunmaması anlamına geliyor.
- Bilinen açık taraması, bir bağımlılığın bildirilen sürümünü modellenmiş bir eşikle
  karşılaştırıyor; tablo eksikse tarama hiçbir şey bulamıyor.
- Bu ders boyunca kullanılan ölçü hep aynıydı: bir aracın sessiz kalması, aradığı şeyin
  bulunmadığını gösteriyor, o şeyin **var olmadığını** göstermiyor.

## Sonraki Adım

Bu derste ölçülen çözümleyici, dilin kendi sınırları içinde kaldı — aradığı kalıplar hep Go
kaynağının kendi söz dizimine ve tipine aitti. Kursun kapanış dersi bu sınırı aşıyor: bir Go
programı başka bir dilin kütüphanesini çağırdığında, çözümleyicinin, çözümleyicinin göremediği
bir sınır daha açılıyor. Sıradaki soru, dilin kendi sınırının ötesine geçmenin bedelinin hangi
araçla, hangi sayıyla görüneceği.
