
Mon Sep 28 2026

Selam! ☕ Otur, biraz sohbet edelim.
Geçen ay prod'da yaşadığım bir hikaye anlatayım. Saat 02:14'te telefon çaldı. Diğer uçta on-call arkadaşım: "Hocam, API yanıt vermiyor, CPU %100, memory de tam dolu..." Ben de yorgun gözlerle loglara baktım — tek bir endpoint'in sonsuz döngüye girmesi tüm cluster'ı yutmuş.
Ne oldu o gece?
Aslında sorun büyük bir mimari hatası değildi. Sadece küçük, gözden kaçan bir detaydı. Ama o detay, production'da ketre etkisi yarattı.
İşte bu yüzden bu konu bizim işimizi etkiliyor:
✅ Kazaları önlemek için — "ben de yaşadım" demek yerine, önceden görmek için
✅ On-call gecelerini rahatlatmak için — 02:00'te panic yerine planlı müdahale için
✅ Kod review'larda neye odaklanmalıyız bilmek için — "bu çalışıyor mu?" yerine "bu nasıl çöker?" sormak için
Hazırsan, pratik bir örnek üzerinden gidip bu detayları beraber inceleyelim. Hadi başlayalım! 🚀
Ghost dependency (hayalet bağımlılık), projenizin doğrudan pom.xml ya da build.gradle dosyasında yazmayan ama transitif olarak classpath’e giren kütüphanelerdir.
Kısaca: “Ben bunu eklemedim, niye burda?” diyeceğiniz bağımlılıklardır 🎯.
pom.xml – sadece library-a eklendi:
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>library-a</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
library-a içinde şu bağımlılık var (siz yazmadınız):
<dependency>
<groupId>com.example</groupId>
<artifactId>library-b</artifactId>
<version>2.3.1</version>
</dependency>
Sonuç: mvn dependency:tree çalıştırınca:
com.example:my-app:jar:1.0.0
└─ com.example:library-a:jar:1.0.0:compile
└─ com.example:library-b:jar:2.3.1:compile ← Ghost dependency!
Ne oluyor? library-b projenizde gözükmez, ama classpath’te durur ❗️.
build.gradle – sadece library-a eklendi:
dependencies {
implementation 'com.example:library-a:1.0.0'
}
library-a kendi pom/module dosyasında library-b’yi çeker.
Sonuç: ./gradlew dependencies çıktısında:
compileClasspath
\--- com.example:library-a:1.0.0
\--- com.example:library-b:2.3.1 ← Ghost dependency!
Nasıl çalışıyor? Gradle da transitif bağımlılıkları otomatik indirir, siz bildirmezsiniz ✅.
İpucu: mvn dependency:tree veya ./gradlew dependencies ile tüm ağacı görüp, isterseniz exclusion / exclude ile engelleyebilirsiniz 🛠.
Hazırsan zaman çizelgesi üzerinden hızlıca geçelim 👇
Özetle: 2019 sonrası Oracle JDK “ücretsiz” değil artık. Prod’da güvenli ve maliyetsiz kalmak için OpenJDK ailesine yönelmek en akıllı hamle 🚀
Hazırsan bu konuyu gerçek bir senaryo üzerinden inceleyelim 🎯.
Bir logging kütüphanesi eklediğimizde, arka planda başka bir kütüphane daha çekiliyor ve o da Oracle JDK’ya özgü sınıfları (örneğin tools.jar içindeki com.sun.tools.*) projenize sızdırıyor. Bu durum, açık kaynak JDK (OpenJDK, Adoptium vb.) kullandığınızda ClassNotFoundError veya modül erişim hataları olarak karşınıza çıkıyor.
tools.jar dosyasını kullanır.system scope ile çözer — yani sizin JDK’nızın lib/tools.jar dosyasından alır.tools.jar yok ⇒ derleme/çalışma anında hata.$ mvn dependency:tree -Dverbose -Dincludes=com.sun:tools
[INFO] com.example:my-app:1.0.0
[INFO] \- ch.qos.logback:logback-classic:1.2.11
[INFO] \- org.codehaus.janino:janino:3.1.6
[INFO] \- com.sun:tools:1.8.0 (system)
Ne oluyor burada?
logback-classic → janino → com.sun:tools zinciri oluşuyor.system kapsamı, yerel JDK’nın tools.jar’ına işaret eder.com.sun.tools.javac.*) içerir.| Durum | Ne olur? |
|---|---|
| Geliştirme Oracle JDK ile yapılır | Her şey yolunda görünür ✅ |
| CI/CD veya Production OpenJDK/Adoptium ile çalışır | ClassNotFoundException: com.sun.tools.javac.Main ⛔ |
| Modüler JDK (9+) | module java.compiler bulunamaz → IllegalAccessError |
mvn dependency:tree (veya gradle dependencies) ile com.sun:tools, sun.misc, com.sun.management gibi iç paketleri arayın.system scope varsa şüphe edin — bu genelde JDK içi bir jar’ı işaret eder.janino gibi derleyiciye ihtiyaç duyan kütüphaneleri opsiyonel yapın veya alternatif (ör. logback yerine log4j2 + log4j2-jul bridge) değerlendirin.Geçişli bağımlılıklar (transitive dependencies) sessizce Oracle JDK’ya özgü sınıfları projenize sokabilir.
Bir logging kütüphanesi eklerkenlogback‑classic → janino → tools.jarzinciri oluşuyorsa, OpenJDK’de patlamak kaçınılmaz hale gelir.
Çözüm: Bağımlılık ağacınızı düzenli tarayın, system scope’lu transitif bağımlılıkları explicit exclude edin veya JDK‑agnostik alternatifleri tercih edin.
Bir sonraki bölümde bu tür sızıntıları otomatik tespit eden araçları (OWASP Dependency‑Check, jdeps, jlink vb.) inceleyeceğiz. 🚀
SBOM (Software Bill of Materials), projenizdeki tüm bileşenleri, sürümlerini ve lisanslarını listeleyen bir “malzeme listesi” gibidir.
Düşünün: bir araba üretirken hangi parçaların hangi tedarikçiden geldiğini bilmek, hatalı bir parça çıkarıldığında hızlı müdahale sağlar. Yazılımda da durum aynı 🚗🔧.
| Araç | Güçlü Yönler ✅ | Zayıf Yönler ❌ |
|---|---|---|
| OWASP Dependency‑Check | • Çok geniş ekosistem desteği (Maven, Gradle, npm, NuGet…) • NVD veritabanı ile CVE eşleştirmesi | • Tarama süresi uzun olabilir • Hayalet bağımlılıkları (transitive, build‑time only) bazen kaçar |
| CycloneDX (format + tooling) | • Standartlaşmış, makine okunabilir JSON/XML • Çoklu dil/ekosistem desteği | • Kendi tarayıcısı yok; genellikle Syft veya Trivy ile üretilir |
| Syft | • Çok hızlı, container image, tar.gz, dir vb. her ortamı tarar • Çıktı formatları: CycloneDX, SPDX, JSON | • Derin statik analiz yok; sadece paket yöneticisi meta verilerine güvenır |
| Trivy | • Hem SBOM üretir hem de vuln scanning yapar • Container image, fs, git repo, Kubernetes hedefleri | • Çok fazla false‑positive üretebilir (özellikle eski sürümlerde) |
Kısaca: Hepsi “paket yöneticisi kilit dosyalarını” (package‑lock.json, pom.xml, go.mod…) okur. Eğer bir bağımlılık build sırasında indirilir ama kilit dosyasına yazılmazsa (ör. bir plugin, script ya da
npm install --ignore-scripts), araçlar onu görmez 👻.
esbuild, webpack plugin’leri) node_modules’e yazılmaz.import()), require çağrıları statik analizden kaçar.Pratik ipucu 🛠
syft dir:. -o cyclonedx-json > sbom.json && trivy fs --sbom sbom.json .trivy image) bir strateji ekleyin.Hazırsan bir sonraki bölümde CI/CD entegrasyonu ve otomatize raporlama üzerine konuşalım! 🚀
Hazırsan terminali açalım ve hayalet bağımlılıkları (örneğin Oracle JDBC sürücüsü) nasıl yakalanır görelim 🎯
dependency:tree tüm bağımlılık ağacını basar-Dincludes=com.oracle:* filtresiyle sadece com.oracle paketlerini gösterirmvn dependency:tree -Dincludes=com.oracle:*
Ne oluyor?
Maven sadece Oracle ile ilgili satırları listeler, geri kalanı gizler ⛔
dependencies --configuration compileClasspath derleme sınıf yolundaki bağımlılıkları yazargrep -i oracle ile çıktıyı oracle kelimesi içeren satırlara indirir./gradlew dependencies --configuration compileClasspath | grep -i oracle
Nasıl çalışıyor?
Gradle önce tam ağacı basar, sonra grep sadece Oracle ilgili satırları bırakır ✅
mvn dependency:tree -Dincludes=com.oracle:* && ./gradlew dependencies --configuration compileClasspath | grep -i oracle
&& sayesinde Maven başarılıysa Gradle komutu çalışır 🔁Küçük ipucu: Çıktıyı bir dosyaya yönlendirip (> oracle-deps.txt) daha sonra incelemek de kolaydır 🛠
Oracle JDK’nın ticari lisansı, üretim ortamlarında sürpriz maliyetler yaratabiliyor.
Bu yüzden ücretsiz ve uzun süre desteklenen (LTS) dağıtımlara yönelmek en sağlıklı yol.
Hepsi OpenJDK tabanlı, bu nedenle kodunuzda herhangi bir değişiklik yapmanıza gerek kalmaz. Sadece bağımlılıklarınızdan Oracle JDK’yı çıkarıp yukarıdakilerden birini eklemeniz yeterli.
Önce sorunu netleştirelim: pom.xml içindeki herhangi bir transitif bağımlılık com.oracle grubunu getirirse, lisans ihlali riski doğar.
Bunu engellemek için exclusion kullanıyoruz.
<dependency>
<groupId>org.some.library</groupId>
<artifactId>some-library</artifactId>
<version>1.2.3</version>
<exclusions>
<exclusion>
<groupId>com.oracle</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
Ne oluyor burada?
<exclusion> bloğu, bu kütüphaneye gelen tüm Oracle artefactlerini (*) engeller.Gradle tarafında da aynı mantıkla, tüm konfigürasyonlar için bir kural koyuyoruz.
configurations.all {
exclude group: 'com.oracle'
}
Nasıl çalışıyor?
configurations.all → projedeki her dependency konfigürasyonuna (implementation, testImplementation, vs.) uygulanır.exclude group: 'com.oracle' → Oracle grubuna ait herhangi bir artefact indirilmesini engeller.Sadece dışlama yetmez; sürüm tutarlılığı ve politika kontrolü de lazım.
| Yöntem | Ne İşe Yarar? | Küçük Bir İpucu |
|---|---|---|
dependencyManagement (Maven) |
Merkezi versiyon tanımlaması, alt modüllerden bağımsız | Parent pom’da <dependencyManagement> bloğu koyun, çocuk modüller sadece <artifactId> yazın. |
platform / enforcedPlatform (Gradle) |
BOM (Bill of Materials) ile versiyon kilitleme | implementation(enforcedPlatform("org.springframework.boot:spring-boot-dependencies:3.2.0")) |
| Maven Enforcer Plugin | Kuralları build sırasında zorla (ör. yasaklı grup, minimum Java sürümü) | <rule><bannedDependencies><excludes>com.oracle:*</excludes></bannedDependencies></rule> |
| Gradle Dependency Verification / Custom Rules | İmza doğrulama, yasaklı bağımlılık kontrolü | dependencyVerification { verify = true } + verificationMetadata dosyası |
Özetle:
Böylece hem lisans riskini ortadan kaldırırsınız hem de sürüm kaosunu önlersiniz. 🚀
Hadi kabul edelim: her push'ta manuel sbom çalıştırmak ya da lisansları tek tek kontrol etmek sürdürülebilir değil 😅. Pipeline'ınıza bu adımları bir kere ekleyin, sonra unutun — sistem sizin yerinize uyarı versin.
Aşağıdaki workflow, her push ve pull request'te otomatik olarak:
name: SBOM & License Scan
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
permissions:
contents: read
security-events: write # SARIF upload için gerekli
pull-requests: write # PR yorumu atabilmek için
jobs:
sbom-and-scan:
runs-on: ubuntu-latest
steps:
- name: 📥 Checkout kod
uses: actions/checkout@v4
- name: 🛠 SBOM üret (Anchore Syft)
uses: anchore/sbom-action@v0
with:
format: spdx-json
output-file: sbom.spdx.json
# İsterseniz 'cyclonedx-json' da kullanabilirsiniz
- name: 📦 SBOM artifact olarak kaydet
uses: actions/upload-artifact@v4
with:
name: sbom-report
path: sbom.spdx.json
retention-days: 30
- name: 🔍 Dependency-Check tarama (CVE + Lisans)
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'My Awesome Project'
path: '.'
format: 'HTML,JSON,SARIF'
out: 'dependency-check-report'
# failOnCVSS: 7 # İsterseniz belirli skor üstünde pipeline'ı kırabilirsiniz
- name: 📤 Dependency-Check raporlarını artifact et
uses: actions/upload-artifact@v4
with:
name: dependency-check-reports
path: dependency-check-report/
retention-days: 30
- name: 📋 SARIF'i GitHub Security'e yükle (Code Scanning sekmesinde görünsün)
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: dependency-check-report/dependency-check-report.sarif
category: dependency-check
Ne oluyor burada?
anchore/sbom-action@v0 → Syft motoruyla hızlı, tam ve standart SBOM üretir. spdx-json çıktısı downstream araçlar (ORAS, cosign, vb.) ile uyumlu.dependency-check/Dependency-Check_Action@main → NVD veritabanını indirir, bağımlılıklarınızı tarar, CVE ve lisans bulgularını HTML/JSON/SARIF olarak çıkarır.github/codeql-action/upload-sarif → Bulguları GitHub Security > Code scanning sekmesine basar; PR'de "Checks" kısmında da görünebilir..gitlab-ci.yml içine şu job'ları ekleyin:
sbom:
stage: security
image: anchore/syft:latest
script:
- syft packages dir:. -o spdx-json=sbom.spdx.json
artifacts:
paths: [sbom.spdx.json]
expire_in: 30d
reports:
sbom: sbom.spdx.json # GitLab 16+ SBOM raporu olarak tanır
dependency_check:
stage: security
image: owasp/dependency-check:latest
script:
- dependency-check --project "My Project" --scan . --format "HTML,JSON,SARIF" --out dependency-check-report
artifacts:
paths: [dependency-check-report/]
expire_in: 30d
reports:
sast: dependency-check-report/dependency-check-report.sarif # GitLab SAST sekmesinde görünür
Küçük ipucu: GitLab'de reports:sbom ve reports:sast anahtarları sayesinde sonuçlar Merge Request arayüzünde, "Security Reports" widget'ında görünür. Ayrı bir dashboard kurmaya gerek yok ✅.
pipeline {
agent any
stages {
stage('SBOM') {
steps {
sh '''
docker run --rm -v "$PWD:/src" anchore/syft:latest \
packages dir:/src -o spdx-json=/src/sbom.spdx.json
'''
archiveArtifacts artifacts: 'sbom.spdx.json', fingerprint: true
}
}
stage('Dependency-Check') {
steps {
sh '''
docker run --rm -v "$PWD:/src" owasp/dependency-check:latest \
--project "My Project" --scan /src --format "HTML,JSON,SARIF" --out /src/dc-report
'''
archiveArtifacts artifacts: 'dc-report/**', fingerprint: true
// SARIF'i Jenkins Warnings Next Generation plugin ile okuyabilirsiniz
}
}
}
}
Docker imajlarını kullanmak, agent'ınıza ekstra araç kurma derdine son verir 🎉.
| Özellik | Ne sağlar? |
|---|---|
| Her PR/deploy'da otomatik tarama | Geliştirici kod push'larken "bu kütüphane GPL-3.0, dikkat!" uyarısını anında alır |
| SBOM artifact | Üretimde hangi paketler var? 6 ay sonra Log4j çıkarsa 5 dakikada cevap verirsiniz |
| SARIF entegrasyonu | GitHub/GitLab/Jenkins UI'sinde tek tıkla CVE listesi, filtreleme, düzeltme takibi |
| Policy gate (opsiyonel) | failOnCVSS: 7 veya lisans allow-list ile pipeline'ı kırabilir, merge'i bloke edebilirsiniz |
Son bir tavsiye: İlk başta failOnCVSS kapatın, sadece rapor toplayın. Ekip alıştıktan sonra eşik değerini kademeli artırın. Aksi takdirde "pipeline sürekli kırmızı, bypass edelim" kültürü oluşur ⛔.
Hazırsanız bir sonraki commit'inizde bu workflow'u ekleyin — erken uyarı sisteminiz canlı 🚀.
Hazırsan bir alışkanlık daha ekleyelim rutininize: haftada 15 dakika, dependency health check 🩺
Kahvenizi alırken, CI/CD pipeline'ınız çalışırken ya da code review beklerken yapabileceğiniz minik bir ritual. Büyük fark yaratıyor, inanın.
npm audit / yarn audit / pnpm audit — hızlı bir tarama, critical/high bulgular var mı?npm outdated — major version atlamaları var mı? Breaking change riski değerlendir.license-checker (veya pnpm licenses list) — GPL/AGPL gibi copyleft lisanslı paket sızmış mı?package-lock.json / pnpm-lock.yaml — anlamsız diff'ler var mı? (Bazen lockfile bozuluyor, gözden kaçırıyoruz.)depcheck veya knip ile kullanmadığınız paketleri temizleyin.İpucu: Bu adımları bir
Makefiletarget'ına ya da npm script'ine alıpnpm run health-checkdiyerek çalıştırın. Otomasyon = alışkanlık olma ihtimali artar 🔁
Çünkü sürekli küçük adımlar, yılda bir gelen "büyük güvenlik denetimi"nden çok daha etkili. Vulnerability'ler erken yakalanır, lisans riski prod'a gitmez, technical debt birikir ama yönetilebilir kalır.
Küçük bir alışkanlık, büyük bir facia önler. Bu hafta Cuma kahvesiyle başlayın, gelecekteki siz size minnet edecek. 🚀
Bu içerik tamamen yapay zeka destekli otomasyon sistemi ile üretilmiştir.
All rights reserved