Gölge API ve Varlık Envanteri: Saldırı Yüzeyini Haritalamak

Bilmediğin endpoint'i koruyamazsın: kod, trafik ve DNS kaynaklarından saldırı yüzeyini çıkaran envanter kayıt formatı ve kapsama metriği.

18 dk okuma
ibrahimsql
3.503 kelime

Gölge API ve Varlık Envanteri: Saldırı Yüzeyini Haritalamak#

Test listesinde olmayan bir endpoint, test edilmiş sayılmaz. Çoğu ekip API güvenliğini birkaç bilinen rotaya bakarak değerlendirir: giriş, kullanıcı profili, ödeme, admin paneli. Oysa üretimdeki gerçek saldırı yüzeyi bunlardan ibaret değildir. Eski sürümler, dahili araçlar için açılmış yollar, mobil sürümün kullandığı gizli parametreler ve dokümana hiç girmemiş rotalar aynı ağda yaşar.

Bu yazı, saldırı yüzeyini tahminle değil kayıtla yönetmeyi anlatır. Amaç tek bir liste çıkarmak değil, listenin nasıl üretildiğini, nasıl güncel tutulduğunu ve test kapsamına nasıl bağlandığını göstermektir. Envanter bir Excel sayfası değil, kod ve trafikten beslenen bir veri setidir.

Gölge API kavramı burada dar anlamda kullanılır: envanterde kaydı olmayan ama istek alan her HTTP arayüzü. Buna unutulmuş sürüm yolları, dahili debug uçları, mobil uygulamaya özel endpointler ve üçüncü taraf geri çağrılar dahildir. Kayıt yoksa sahiplik de yoktur, test de yoktur. Yazının akışı şöyledir: önce envanterin neden testten önce geldiği, sonra envanteri besleyen altı keşif kaynağı, ardından kayıt formatı, fark otomasyonu, sürüm disiplini, kapsama metriği ve son olarak sınırlar.

1. Önce envanter: bilinmeyen endpoint korunamaz#

Yetkilendirme testi, bilinen bir endpoint listesi üzerinden yürür. Liste eksikse test sonucu da eksiktir, hem de sessizce eksiktir. Rapor "kritik bulgu yok" der, çünkü raporun bakmadığı yerler vardır. Bu durumun adı gölge API problemidir ve üç kalıpta sık görülür.

1.1 Unutulmuş sürüm yolları#

/v2 yayına alınır, /v1 kapatılmayı bekler ve bekleme yıllarca sürer. Eski sürüm genelde eski yetkilendirme mantığını da taşır: yeni eklenen tenant filtresi /v2 tarafına yazılır, /v1 tarafındaki denetçi metodu olduğu gibi kalır. Saldırgan için sürüm numarası bir ayrıntıdır, istek alan her yol giriştir.

Bu kalıp özellikle kademeli geçişlerde büyür. Mobil uygulamanın eski sürümleri /v1 kullanmaya devam eder, ekip geriye dönük uyumluluk adına eski yolu kapatamaz. Kapatılamayan yolun kaydı tutulmuyorsa, kimse onun test matrisinde olup olmadığını da sorgulamaz.

1.2 Dahili debug ve destek rotaları#

Olay anında hızlı bakmak için açılan uçlar tipik örnektir: kullanıcı arama, sipariş detayı, toplu dışa aktarma. Başlangıçta IP kısıtı ya da geçici bir anahtar vardır, sonra kısıt gevşer, anahtar koda gömülür, rota unutulur. Bu rotalar dokümana girmez çünkü "ürün özelliği" sayılmazlar.

Sorun, bu rotaların sıradan rotalardan daha fazla veri göstermesidir. Debug çıktısı bol alan içerir: ilişkili kayıtlar, iç notlar, ham durumlar. Yetki kontrolü eksikse sızan veri de fazla olur. API yetkilendirme hataları yazısındaki nesne seviyesi kontroller, tam da bu tür yollarda kırılır.

1.3 Yalnızca mobilin kullandığı endpointler#

Web ekibi ile mobil ekip aynı API'yi kullanıyor gibi görünür, ama pratikte mobil uygulama ek parametreler ve ek yollar kullanır. Cihaz kaydı, sessiz bildirim onayı, uygulama içi satın alma doğrulama gibi uçlar web akışında hiç görünmez. Web trafiğine bakarak envanter çıkaran ekip bunları kaçırır. Mobil uçlar ayrıca farklı kimlik taşıma biçimine sahiptir: uzun ömürlü yenileme jetonları, cihaza bağlı anahtarlar, mağaza makbuzuyla doğrulama. Test planı web oturumuna göre yazıldıysa mobil uçlar plan dışında kalır.

1.4 Somut senaryo: dokümanda olmayan ihracat ucu#

Şu akışı düşün: bir e-ticaret panelinde sipariş listesi sayfası vardır, sayfa /v2/orders yolunu kullanır ve testler buraya odaklanır. Aynı panelde bir dönem toplu çıktı özelliği istenmiştir, geliştirici /v1/orders/export?format=csv yolunu eklemiştir. Özellik arayüzden kaldırılır ama rota kodda kalır.

Aylar sonra bu yol hâlâ istek alır. Eski mobil sürüm arka planda senkron için çağırır, bazı kayıtlı betikler de çağrıyı sürdürür. Yeni tenant filtresi /v2 katmanına yazılmıştır, /v1 tarafındaki denetçi eski mantıkla çalışır. Sonuç: test edilen yol güvenli görünür, test edilmeyen yol aynı veriyi farklı kapıdan verir.

Bu senaryonun dersi nettir: güvenlik kararı endpoint bazında verilir, ekran bazında değil. Ekrandan kalkan özellik, API'den kalkmış sayılmaz. Kayıt formatı ve fark otomasyonu bölümleri bu boşluğu kapatmak içindir.

Gölge kalıpTipik kaynakNeden test dışı kalırİlk belirti
Unutulmuş /v1Kademeli sürüm geçişiTestler güncel sürüme yazılırLogda eski yola istek
Debug rotasıOlay anı araçlarıDokümana girmezDokümanda olmayan yol
Mobil-özel uçUygulama sürümleriWeb trafiğiyle taramaPakette fazladan yol
Destek paneli ucuİç araçlarÜrün sayılmazAlt alan adında API
Webhook alıcıÜçüncü taraf entegrasyonGelen yön unutulurİmzasız istek kabulü
Toplu iş ucuZamanlanmış görevlerEtkileşimli test görmezGece loglarında trafik

2. Keşif kaynakları#

Tek kaynakla tam envanter çıkmaz. Kodun söylediği ile trafiğin söylediği farklıdır, DNS'in söylediği ise bambaşkadır. Aşağıdaki altı kaynak birbirini tamamlar: her birinin yakaladığı ve kaçırdığı ayrıdır.

2.1 Rota dosyası taraması#

Kaynak kod, niyetin aynasıdır. Rota tanımları hangi yolların var olabileceğini söyler. Betik yaklaşımı basittir: rota dosyalarını gez, dekoratör ve kayıt çağrılarındaki yol kalıplarını topla, HTTP metoduyla eşleştir, çıktıyı listeye yaz.

# Rota tanımlarını kaba kuvvetle toplamak için ilk adım rg -n --no-heading \ -e "@(app|router)\.(get|post|put|patch|delete)" \ -e "(app|router)\.(get|post|put|patch|delete)\(" \ -e "path\s*=\s*[\"']/api" \ src/ | sort -u > /tmp/route-hits.txt wc -l /tmp/route-hits.txt head -n 20 /tmp/route-hits.txt

Daha düzenli bir arama için küçük bir Python betiği yazılır. Betik dosya uzantılarına göre ayrışır ve yol ile metodu ayrı sütunlarda raporlar. Amaç mükemmel ayrıştırma değil, hızlı aday listesidir.

# route_scan.py: rota dosyalarından aday endpoint listesi üretir import pathlib import re PATTERNS = [ re.compile(r"""@(?:app|router)\.(get|post|put|patch|delete)\(\s*["']([^"']+)["']"""), re.compile(r"""(?:app|router)\.(get|post|put|patch|delete)\(\s*["']([^"']+)["']"""), re.compile(r"""path\s*=\s*["']((?:/api|/v\d|/internal|/debug)[^"']*)["']"""), ] ROOTS = ["src/routes", "src/api", "app/api", "server/routes"] def scan(root: pathlib.Path): rows = [] for file in root.rglob("*.py"): text = file.read_text(encoding="utf-8", errors="ignore") for rx in PATTERNS: for m in rx.finditer(text): if len(m.groups()) == 2: rows.append((m.group(1).upper(), m.group(2), str(file))) else: rows.append(("?", m.group(1), str(file))) for file in root.rglob("*.ts"): if "node_modules" in str(file): continue text = file.read_text(encoding="utf-8", errors="ignore") for rx in PATTERNS: for m in rx.finditer(text): if len(m.groups()) == 2: rows.append((m.group(1).upper(), m.group(2), str(file))) else: rows.append(("?", m.group(1), str(file))) return sorted(set(rows)) if __name__ == "__main__": found = [] for r in ROOTS: p = pathlib.Path(r) if p.exists(): found += scan(p) for method, path, file in found: print(f"{method:6} {path:45} {file}") print(f"\nToplam aday: {len(found)}")

Bu kaynağın yakaladığı durumlar aşağıdaki maddelerde özetlenmiştir:

  • Kodda tanımlı tüm yollar, dokümana girsin ya da girmesin

  • Metot bilgisi: aynı yolun GET ve POST varyantları ayrı satır olur

  • Dosya konumu: sahibin kim olabileceğini gösterir Bu kaynağın kaçırdığı durumlar aşağıdaki maddelerde sıralanmıştır:

  • Ağ geçidi seviyesinde eklenen yollar kodda görünmez

  • Eklenti ve ara katmanların ürettiği dinamik yollar

  • Kodda duran ama dağıtımda kapalı yollar (ölü kod gürültüsü)

  • Farklı çerçevelerin alışılmadık kayıt biçimleri

2.2 OpenAPI belgesi ile gerçekliğin karşılaştırılması#

OpenAPI belgesi, ekibin dünyayı nasıl gördüğüdür. Gerçek trafik ise dünyanın nasıl olduğudur. İkisi arasındaki fark, envanterin en verimli girdisidir. Karşılaştırma iki yönlü yapılır: belgede olup trafikte olmayan (ölü ya da yanlış belge), trafikte olup belgede olmayan (gölge aday).

# OpenAPI belgesindeki yol sayısını hızlıca görmek için python3 -c " import json spec = json.load(open('openapi.json')) paths = spec.get('paths', {}) print(f'Belgede kayitli yol: {len(paths)}') for p in sorted(paths)[:15]: print(' ', p) "

Belge tarafının yakaladığı durumlar aşağıdaki maddelerde özetlenmiştir:

  • Şema bilgisi: parametre, gövde, yanıt biçimi

  • Kimlik beklentisi: her işlemin güvenlik gereksinimi

  • Sürüm bilgisi: hangi yolun hangi sürüme ait olduğu Belge tarafının kaçırdığı durumlar aşağıdaki maddelerde sıralanmıştır:

  • Belge güncel değilse her şey: belge çürümeye eğilimlidir

  • Belge dışı tutulan iç yollar

  • Belgede joker karakterle gizlenen varyantlar

  • Belgeye hiç girmemiş mobil uçlar

2.3 Proxy geçmişi incelemesi#

Proxy geçmişi, gerçekte neyin çağrıldığını gösterir. Kodun unuttuğunu trafik hatırlar. Test ya da üretim ortamından alınan bir günlük proxy kaydı, belgede olmayan yolları ortaya çıkarır. Burada amaç tek tek istek okumak değil, yol kalıplarını sayıp sıralamaktır.

# Proxy dışa aktarımından yol frekansı çıkarmak için python3 -c " from collections import Counter import re c = Counter() with open('proxy-history.txt') as f: for line in f: m = re.search(r'\s(GET|POST|PUT|PATCH|DELETE)\s(\S+)', line) if m: method, url = m.groups() path = re.sub(r'^https?://[^/]+', '', url).split('?')[0] c[(method, path)] += 1 for (method, path), n in c.most_common(30): print(f'{n:6} {method:6} {path}') "

Proxy kaynağının yakaladığı durumlar aşağıdaki maddelerde özetlenmiştir:

  • Gerçekten istek alan yollar, ölü koddan arındırılmış

  • Parametre ve gövde örnekleri

  • Kimlik başlıklarının fiilen taşınıp taşınmadığı

  • Hata veren gizli yollar (404 ve 403 satırları da sinyaldir) Bu kaynağın kaçırdığı durumlar aşağıdaki maddelerde sıralanmıştır:

  • Kayıt döneminde çağrılmamış seyrek yollar

  • Zamanlanmış görevlerin gece çalıştırdığı uçlar

  • Yalnızca belirli kiracılarda tetiklenen yollar

  • Başka bölgeden sunulan uçlar

2.4 JS paketlerinden endpoint çıkarma#

Ön yüz paketi, API istemcisinin itirafıdır. Derlenmiş JS içinde yol dizgileri durur. Dizgi taramasıyla /api/... kalıpları toplanır, ardından trafik ve belgeyle kesiştirilir. Pakette olup başka hiçbir kaynakta olmayan yol, araştırmaya değer.

# Derlenmis pakette yol dizgilerini aramak icin rg -o --no-filename \ -e '"/api/[^"]+"' \ -e "'/api/[^']+'" \ -e '"/v[0-9]+/[^"]+"' \ -e "'/v[0-9]+/[^']+'" \ dist/ web/.next/static/ 2>/dev/null \ | tr -d "\"'" | sort -u > /tmp/bundle-paths.txt wc -l /tmp/bundle-paths.txt head -n 20 /tmp/bundle-paths.txt

Paket kaynağının yakaladığı durumlar aşağıdaki maddelerde özetlenmiştir:

  • Ön yüzün gerçekten çağırdığı yollar

  • Belgede unutulmuş istemci uçları

  • Deney bayraklarıyla koşullu çağrılan yollar

  • Hata ayıklama derlemesinde kalan ek yollar Bu kaynağın kaçırdığı durumlar aşağıdaki maddelerde sıralanmıştır:

  • Sunucu tarafı çağrılar (zamanlanmış işler, kuyruk tüketiciler)

  • Mobil uygulamanın kullandığı yollar

  • Karartılmış ya da parçalı birleştirilmiş dizgiler

  • Üçüncü taraf geri çağrı uçları

2.5 DNS ve alt alan adı sayımı#

API bazen ana alan adında değil, unutulmuş bir alt alan adındadır: admin-eski, api-test, destek-paneli gibi kayıtlar yıllarca yaşar. DNS sayımı bu kapıları bulur. Sertifika kayıtları ve pasif DNS verisi, alt alan adlarını listeler; her adayın üzerinde neyin istek aldığı yoklanır.

# Alt alan adlarini sertifika kayitlarindan toplamak icin curl -s "https://crt.sh/?q=%25.ornek-sirket.com&output=json" \ | python3 -c " import json, sys try: data = json.load(sys.stdin) except Exception: data = [] names = set() for row in data: for part in str(row.get('name_value', '')).split(): names.add(part.strip().lower()) for n in sorted(names)[:40]: print(n) print(f'Toplam aday alt alan adi: {len(names)}') "

DNS kaynağının yakaladığı durumlar aşağıdaki maddelerde özetlenmiştir:

  • Unutulmuş test ve sahne ortamları

  • Eski yönetici panelleri

  • İç araçların dışa açık kalmış halleri

  • Bölgesel ya da kiracıya özel uçlar Bu kaynağın kaçırdığı durumlar aşağıdaki maddelerde sıralanmıştır:

  • Yol seviyesi ayrıntı (yalnızca ana makine verir)

  • Kimlik ve veri sınıfı bilgisi

  • DNS'e düşmemiş iç ağ uçları

  • Kısa ömürlü kampanya alt alan adları kapanmışsa

2.6 Mobil API trafiği#

Mobil uygulama ayrı bir istemcidir ve ayrı izlenir. Uygulama trafiği bir test cihazından proxy üzerinden geçirilir, sertifika sabitleme varsa test derlemesinde gevşetilir. Toplanan yollar web envanteriyle kesiştirilir: kesişim dışı kalan her yol mobil-özel adaydır.

Bu kaynağın yakaladığı durumlar aşağıdaki maddelerde özetlenmiştir:

  • Yalnızca uygulamanın çağırdığı uçlar

  • Sürüm parametreleri ve cihaz başlıkları

  • Mağaza makbuzu doğrulama gibi mobil akışlar

  • Eski uygulama sürümlerinin hâlâ çağırdığı yollar Bu kaynağın kaçırdığı durumlar aşağıdaki maddelerde sıralanmıştır:

  • Kimsenin kurmadığı eski uygulama sürümlerinin yolları

  • Arka plan senkronun seyrek tetiklediği uçlar

  • Sunucudan sunucuya çağrılar

  • Cihazda gömülü ama artık çağrılmayan ölü yollar Keşif kaynaklarının özeti tabloda toplanır. Tablo, hangi kaynağın hangi boşluğu kapattığını gösterir.

KaynakAna girdiEn güçlü yanıKör noktası
Rota taramasıKaynak kodDokümansız yolu bulurÖlü kod gürültüsü
Belge karşılaştırmaOpenAPIŞema ve güvenlik bilgisiGüncel değilse yanıltır
Proxy geçmişiGerçek trafikFiilen kullanılan yolSeyrek yolları kaçırır
JS paketiDerlenmiş ön yüzİstemci gerçeğiSunucu işlerini görmez
DNS sayımıSertifika ve DNSUnutulmuş alt alan adıYol detayı vermez
Mobil trafikCihaz proxy kaydıMobil-özel uçlarEski sürümleri kaçırır

3. Envanter kayıt formatı#

Liste, kayıt formatı olmadan çürür. Her endpoint için aynı alanlar doldurulur, boş kalan alan da bilgi sayılır: sahibi bilinmeyen uç, sahipsiz uçtur. Aşağıdaki şema en küçük yeterli seti verir.

AlanZorunluAçıklamaÖrnek değer
methodEvetHTTP metoduGET
pathEvetKalıp biçiminde yol/v1/orders/{id}/export
auth_typeEvetKimlik taşıma biçimibearer, cookie, api-key, none
tenant_scopeEvetKiracı ayrımı var mıper-tenant, global, unknown
data_classEvetTaşınan verinin duyarlılığıpublic, internal, personal, payment
ownerEvetSorumlu ekip ya da kişiodeme-ekibi
statusEvetYaşam döngüsü durumuactive, deprecated, unknown
versionHayırSürüm etiketiv1
sourceHayırHangi keşifle bulunduproxy, route-scan, bundle
last_seenHayırSon gözlem tarihi2026-09-28
pii_fieldsHayırKişisel veri alanları["email", "iban"]
notesHayırBağlam notudestek paneli kullaniyor
Alanların anlamı kısaca şöyledir. auth_type değeri none olan her kayıt ayrıca incelenir: herkese açık olması gereken uçlar bellidir (sağlık kontrolü gibi), onun dışındaki none kayıtları aday bulgudur. tenant_scope değeri unknown olan kayıt testte önceliklidir, çünkü kiracı ayrımı doğrulanmamış demektir.

data_class alanı test sırasını belirler. Ödeme ve kişisel veri taşıyan uçlar önce test edilir, herkese açık içerik veren uçlar sonra. Bu sıralama, siber güvenlik risk modeli yazısındaki etki odaklı düşünmeyle aynı yöndedir: önce zararı büyük olan yer kapatılır.

status alanı üç değer alır, ikisi yetmez. active desteklenen yolu, deprecated kapatılma sürecindeki yolu, unknown ise henüz kimseyle doğrulanmamış yolu gösterir. Yeni keşfedilen her kayıt unknown olarak girer, sahip ekip doğrulayana kadar bu değerde kalır.

Bu şemaya uygun örnek bir kayıt aşağıda verilmiştir:

{ "method": "GET", "path": "/v1/orders/{id}/export", "auth_type": "bearer", "tenant_scope": "unknown", "data_class": "personal", "owner": "unknown", "status": "unknown", "version": "v1", "source": "proxy", "last_seen": "2026-09-28", "pii_fields": ["email", "phone", "address"], "notes": "Proxy kaydinda goruldu, belgede yok. Sahip ekip dogrulanacak." }

İkinci örnek, sağlıklı bir kaydı gösterir. Alanların tamamı doludur ve durum nettir.

{ "method": "GET", "path": "/v2/orders/{id}", "auth_type": "bearer", "tenant_scope": "per-tenant", "data_class": "personal", "owner": "siparis-ekibi", "status": "active", "version": "v2", "source": "openapi", "last_seen": "2026-10-01", "pii_fields": ["email", "phone"], "notes": "Tenant filtresi sunucu tarafinda uygulaniyor, test matrisinde." }

Kayıtlar tek bir JSON Lines dosyasında tutulur. Her satır bir endpointtir, sürüm denetimine girer. Bu düzenin yararı basittir: fark otomasyonu dosyayı okur, metrik dosyadan hesaplanır, inceleme talebi dosya üzerinden açılır.

# Envanter dosyasinin temel saglik kontrolu python3 -c " import json rows = [json.loads(l) for l in open('api-inventory.jsonl') if l.strip()] print(f'Toplam kayit: {len(rows)}') from collections import Counter print('Durum dagilimi:', dict(Counter(r['status'] for r in rows))) print('Sahipsiz kayit:', sum(1 for r in rows if r.get('owner') == 'unknown')) print('Kapsam bilinmeyen:', sum(1 for r in rows if r.get('tenant_scope') == 'unknown')) print('Kimliksiz kayit:', sum(1 for r in rows if r.get('auth_type') == 'none')) "

4. Belge-trafik fark otomasyonu#

Envanterin güncel kalması için belge ile trafik düzenli karşılaştırılır. Yaklaşım üç adımdır: belgeyi oku, trafiği oku, kesişim dışını raporla. Karşılaştırma yol kalıbı seviyesinde yapılır, ham URL seviyesinde değil. /orders/123 ile /orders/456 aynı kalıptır: /orders/{id}.

Önce yollar normalize edilir. Sayılar, UUID kalıpları ve tarih biçimleri yer tutucuya çevrilir. Bu adım olmadan her istek ayrı yol gibi görünür ve rapor çöp olur. Normalleştirme kuralları küçük tutulur ve dosyada saklanır.

Sonra belge yolları ile gözlenen yollar kesiştirilir. Belgede olmayıp trafikte olan her kalıp, gölge adaydır. Trafikte olmayıp belgede olan her kalıp ise ölü ya da yanlış belgedir. Her iki yön de raporlanır, çünkü ikisi de bakım sinyalidir. Aşağıdaki sözde kod bu akışı gösterir. TypeScript benzeri yazılmıştır, doğrudan çalıştırma amacı taşımaz, üretim betiğinin iskeleti olarak okunmalıdır.

// diff.ts: OpenAPI yollari ile proxy gozlemlerini karsilastirir type ObservedHit = { method: string; rawPath: string; count: number }; type Finding = { kind: "undocumented" | "unstaged-doc" | "method-mismatch"; method: string; path: string; count: number; }; const ID_LIKE = [ /\/\d+(?=\/|$)/g, /\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?=\/|$)/gi, /\/\d{4}-\d{2}-\d{2}(?=\/|$)/g, ]; function normalize(rawPath: string): string { const clean = rawPath.split("?")[0].replace(/\/+$/, "") || "/"; let out = clean; for (const rx of ID_LIKE) { out = out.replace(rx, "/{id}"); } return out; } function openApiKeys(spec: any): Set<string> { const keys = new Set<string>(); const paths = spec.paths ?? {}; for (const [path, ops] of Object.entries<any>(paths)) { const normalized = normalizeSpecPath(path); for (const method of Object.keys(ops)) { if (["get", "post", "put", "patch", "delete"].includes(method)) { keys.add(method.toUpperCase() + " " + normalized); } } } return keys; } function normalizeSpecPath(specPath: string): string { return specPath.replace(/\{[^}]+\}/g, "{id}"); } function diff( spec: any, hits: ObservedHit[] ): Finding[] { const documented = openApiKeys(spec); const seen = new Map<string, number>(); for (const h of hits) { const key = h.method.toUpperCase() + " " + normalize(h.rawPath); seen.set(key, (seen.get(key) ?? 0) + h.count); } const findings: Finding[] = []; for (const [key, count] of seen) { const [method, ...rest] = key.split(" "); const path = rest.join(" "); if (!documented.has(key)) { findings.push({ kind: "undocumented", method, path, count }); } } for (const key of documented) { if (!seen.has(key)) { const [method, ...rest] = key.split(" "); findings.push({ kind: "unstaged-doc", method, path: rest.join(" "), count: 0, }); } } return findings.sort((a, b) => b.count - a.count); }

Betik çıktısı insan için okunur olur. Her satır bir karar ister: envantere ekle, belgeyi düzelt ya da yolu kapat. Kararsız satır unknown olarak envantere girer ve sahibe atanır.

# diff-report.txt (ornek cikti) UNDOCUMENTED GET /v1/orders/{id}/export 412 istek UNDOCUMENTED POST /internal/support/lookup 88 istek UNDOCUMENTED GET /api/mobile/receipt/verify 61 istek UNSTAGED-DOC DELETE /v2/orders/{id} 0 istek UNSTAGED-DOC PATCH /v2/users/{id}/role 0 istek

Raporun çalışma ritmi önemlidir. Her sürümde bir kez, ayrıca haftalık zamanlanmış iş olarak çalışır. Eşik değer konur: örneğin 10 isteği geçen belgelenmemiş yol otomatik olarak inceleme kaydı açar. Eşiğin altı sessizce izlenir, üstü insana gider.

5. Sürüm ve kullanımdan kaldırma disiplini#

Unutulmuş /v1 problemi teknik değil, süreç problemidir. Kapatma kararı verilmez, ertelenir, erteleme kalıcı olur. Çözüm, kapatmayı tek olay değil dört aşamalı iş olarak tanımlamaktır: duyur, ölç, engelle, kaldır. Her aşamanın kaydı envanterde tutulur.

Duyur aşamasında yol deprecated olarak işaretlenir. Yanıt başlığına kapatma bilgisi eklenir, istemci sahiplerine tarih verilir. Başlık küçük bir iştir ama etkilidir: loglarda hangi istemcinin eski yolu kullandığı görülür.

# Kapatma surecindeki yanit basliklari (ornek) Deprecation: true Sunset: Wed, 01 Apr 2026 00:00:00 GMT Link: </v2/orders/{id}>; rel="successor-version"

Ölç aşamasında kullanım izlenir. Hangi istemci sürümü, hangi kiracı, günde kaç istek: bu sayılar kapatma tarihinin gerçekçi olup olmadığını gösterir. Kullanım sıfıra inmiyorsa tarih ertelenmez, engelleme planlanır. Erteleme, ölçüm olmadan yasaktır.

Engelle aşamasında yol varsayılan kapalı olur. İstisna gerektiren istemci, izin listesiyle geçici erişim alır. Her istisna envanter kaydına not düşülür ve bitiş tarihi yazılır. Süresiz istisna kabul edilmez.

Kaldır aşamasında kod silinir, test silinir, kayıt removed olur. Kayıt dosyadan silinmez, durumu değişir: geçmişte böyle bir yolun var olduğu bilgisi korunur. Denetimde "bu yol ne oldu" sorusunun cevabı hazırdır.

Kapatma sürecinde izlenecek kontrol listesi aşağıda verilmiştir:

  • Envanterde deprecated işaretlendi, tarih yazıldı
  • Yanıt başlıkları eklendi (Deprecation, Sunset)
  • İstemci sahiplerine tarih bildirildi
  • Günlük kullanım sayımı başladı
  • Eşik üstü kullanıcılarla geçiş planı yapıldı
  • Engelleme tarihi envantere yazıldı
  • İzin listesi istisnaları tarihli kaydedildi
  • Kod ve test silindi, kayıt removed oldu

6. Kapsama metriği: test matrisine giren oran#

Envanterin değeri, teste bağlanınca ortaya çıkar. Metrik tek cümledir: envanterdeki yolların yüzde kaçı yetkilendirme test matrisine girmiş durumda. Payda envanter, pay test edilen kayıtlardır.

kapsama = test_matrisindeki_envanter_kaydi / toplam_aktif_envanter_kaydi

Örnek: 180 aktif kayıt varsa ve 135 tanesi matriste satır olarak yer alıyorsa kapsama 0.75 olur. Hedef sayı yönetim kararıdır, bu yazı eşik önermez. Önemli olan sayının her sürümde yeniden hesaplanmasıdır.

Metrik veri sınıfına göre kırılır. Ödeme verisi taşıyan uçlarda kapsama ile herkese açık içerik uçlarındaki kapsama aynı tabloda ayrı satır olur. Tek ortalama, kritik açığı saklar: genel oran yüksek görünürken ödeme uçları boş kalabilir.

KırılımAktif kayıtMatristeKapsama
Ödeme verisi22200.91
Kişisel veri64480.75
İç veri51300.59
Herkese açık43370.86
Toplam1801350.75
Takip sürüm bazında yapılır. Her sürüm etiketi için metrik bir satır olarak kaydedilir, düşüş varsa sürüm notuna açıklama yazılır. Düşüşün iki olağan nedeni vardır: yeni yollar eklenmiştir ya da eski yollar matristen düşmüştür. İkisi de envanterden okunur, tahmine gerek yoktur.
# Surum bazinda kapsama gecmisini tutmak icin basit duzen cat coverage-history.csv # version,active,tested,coverage # v2.14.0,172,131,0.76 # v2.15.0,180,135,0.75 # v2.16.0,184,150,0.82

Bu metrik, BOLA test matrisi otomasyonu yazısındaki matrisle birlikte okunmalıdır. Matris testin derinliğini verir, envanter testin genişliğini. İkisi birleşince "neyi, ne kadar test ettik" sorusu iki sayı ile cevaplanır: kapsama ve geçen test oranı.

7. Sınırlar ve Kısa sonuç#

Envanter çürümeye eğilimlidir. Kaynak değişir, betik çalışmaz, DNS girdisi eskir, mobil sürüm trafiği toplanmaz. Çürümenin ilacı tek seferlik temizlik değil, zamanlanmış iştir: fark betiği her hafta çalışır, kapsama her sürümde hesaplanır. Aksayan adım görünür olur, görünür olan düzeltilir.

İstemci-tarafı uçlar kısmi kör nokta olarak kalır. Karartılmış paketler, parça birleştirilen dizgiler ve yerel derlemede kalan yollar tam listeye girmeyebilir. Bu boşluk kabul edilir ve kayda yazılır: envanterin "bilinen bilinmeyenler" bölümü de vardır.

Üçüncü taraf geri çağrılar ayrı özen ister. Gelen yöndeki uç, giden entegrasyonun gölgesinde kalır. İmza doğrulaması olmayan alıcı uç, kimliksiz kayıt gibi işlem görür. Sağlayıcı tarafı değiştiğinde yol sessizce farklı davranmaya başlar, bu yüzden alıcı uçların testi sağlayıcı sürüm notuyla birlikte anılır.

Kısa sonuç: korunmayan endpoint genelde bilinmeyen endpointtir. Altı kaynağın çıktısı tek kayıt formatında birleşir, fark otomasyonu listeyi güncel tutar, kapsama metriği testin ne kadarını gördüğünü sayıya döker. Envanter bitmiş bir belge değil, her sürümde yenilenen bir veri setidir.

---
Bu yazıyı paylaş:
TwitterLinkedInFacebook

Ne düşünüyorsun?

Tepki bırakarak geri bildirim ver

İlgili Yazılar