# VC-098 · Veri kaybettiren migration hedef ve değişiklik onayı alınmadan çalışıyor

Şema değişikliği normal yayın adımı gibi çalışıyor ve veri silen komut ayrı değerlendirmeden geçmiyor. Kod geri alınsa bile silinen sütun veya kayıt kendiliğinden geri gelmiyor.

- Önem: ORTA. Etkisi büyük. Nadiren tetiklenir.
- Önem notu: Kalıcı veri kaybettiren değişikliğin canlı hedefte çalışması esas alınır. Her migration yıkıcı değildir. Yalnız boş veya yeniden üretilebilir geliştirme verisi için etki daha düşüktür.
- Eksen ve kategori: Sağlamlık, 15 Gözlem, yedek ve kurtarma
- Yığın: Her yığın
- Yapay zekâ kodunda: ölçülmedi. Dayanak: uzman görüşü.
- Ne zaman bakılır: İlk yayından önce, Her ay
- CWE: CWE-862
- OWASP Top 10:2025: A01:2025 Broken Access Control
- Checklist ifadesi: Veri kaybettiren migration'lar hedef ve değişiklik onayı alınmadan çalışmıyor.
- Son inceleme: 4 Ekim 2026, Komünite editörlüğü
- Adres: https://vibecheck.komunite.com.tr/madde/geri-alinamaz-migration-onaysiz-calisiyor

## 60 saniyelik kontrol

Yalnız kendi uygulamanda ya da yazılı izin aldığın sistemde dene. Bu bir sızma testi değildir.

1. Migration içinde DROP, TRUNCATE ve veri dönüştüren UPDATE yollarını incele.
2. Onayın çalışacak değişiklik ve hedef veritabanına bağlı olduğunu kontrol et.
3. Canlı yazma kimliğine hangi işlerin erişebildiğini doğrula.
4. Son geri yükleme kanıtını ve değişiklik sonrası veri kontrolünü bul.

## Ne oluyor

Kod yayını geri alınabiliyorsa veritabanı değişikliğinin de geri alınabileceği düşünülüyor. Ancak yeni sürüm bir sütunu kaldırmış veya eski kayıtları silmiş olabilir. Önceki uygulama dosyalarını geri koymak o veriyi yeniden üretmez. Migration komutunun başarılı tamamlanması, değişikliğin güvenle geri döndürülebileceği anlamına gelmez.

Yıkıcı değişiklik günlük yayın adımının içinde otomatik çalıştığında, inceleme yalnız kod farkına odaklanabilir. Hangi veritabanına bağlanılacağı, hangi verinin kaybolacağı ve onayın hangi dosyaya ait olduğu belirsiz kalabilir. Genel bir devam onayı daha sonra değiştirilmiş SQL için kullanılırsa incelenen şeyle çalıştırılan şey ayrışır.

Bu madde bütün migration işlemlerini durdurmayı önermez. Yeni nullable sütun eklemekle eski veriyi kaldırmak farklı risk taşır. Amaç veri kaybettiren adım için hedefi, içeriği ve kurtarma yolunu somutlaştırmaktır. Onay kaydı güvenilir bir kaynaktan gelmeli ve canlı yazma kimliği bu sınırı atlayarak başka bir işten kullanılmamalıdır. Dosyada onay yazması tek başına böyle bir sınır kurmaz.

## Gerçek olay

Bu maddede yeni bir AI olayını doğrulanmış vaka olarak eklemiyoruz. GitHub'ın dağıtım ortamı belgesi, ortamı kullanan işlerin zorunlu inceleme gibi koruma kuralları geçmeden ilerlemeyebileceğini anlatıyor. Ortam sırları da onay gerektiren işte onaydan önce kullanılamaz. [Dağıtım koruma kuralları](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments)

Ancak iş akışına ortam adı yazmak bu kuralları kendiliğinden oluşturmaz. İnceleyenler, dal sınırı ve yönetici atlama ayarı ayrıca yapılandırılır. Özelliklerin kullanılabilirliği depo görünürlüğüne ve plana bağlıdır. [Dağıtımı inceleme](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/review-deployments) Bu nedenle örnekte yerel bir onay eşleştirme davranışı gösteriyoruz. GitHub hesabında koruma kuralı kurulduğunu veya gerçek bir Supabase migration işleminin çalıştırıldığını iddia etmiyoruz. Kaynak, uygulanabilecek yetki sınırını ve sınırın önkoşullarını destekler.

## Yapay zekâ bunu neden üretiyor

**Model şema uyuşmazlığını hemen gidermek ister.** Uygulama yeni alan bekliyorsa ajan mevcut şemayı yeni modele benzetmeye odaklanabilir. Eski alanın içindeki verinin iş değeri istemde görünmüyorsa kaldırma komutu teknik temizlik gibi sunulabilir. Veri taşıma, eski sürümle uyumluluk ve bekleme süresi ayrı kabul koşulları olarak yazılmalıdır. Şemanın derlenmesi bunları kanıtlamaz.

**Geliştirme alışkanlığı canlıya taşınır.** Boş yerel veritabanını yeniden kurmak geliştirmede makul olabilir. Aynı komut canlı bağlantıyla çalıştığında sonuç farklıdır. Model komutun hedefini açıkça ayırmadığında yeniden kurma davranışını genel çözüm diye önerebilir. Bağlantı kimliği ve ortamın güvenilir kaynaktan belirlenmesi, yalnız dosya adına güvenmekten daha somut bir denetim sağlar.

**Onay metni değişiklikle eşleşmeyebilir.** Ajan önce bir plan sunup sonra SQL'i güncellediğinde önceki onayı hâlâ geçerli sayabilir. İncelenen dosya ile çalıştırılan dosyanın özeti farklıysa bu kabul yanlıştır. Onayın hedefe, içerik özetine ve geçerlilik süresine bağlı olması bu farkı görünür kılar. İstemcinin kendi ürettiği onay nesnesi güvenilir kanıt sayılmaz.

**Geri alma sözü iki işlemi karıştırır.** Kod sürümüne dönmek, transaction içindeki yazıyı geri almak ve tamamlanmış değişiklikten önceki veriyi yedekten getirmek farklı yollardır. Model bunları aynı rollback sözcüğüyle anlatabilir. Hangi yolun hangi aşamada çalıştığı ayrı yazılmalıdır. Bu açıklamalar üretim mekanizmasına ilişkin yorumlardır. Modeller arasında ölçülmüş hata sıklığı veya belirli bir aracın her zaman böyle davranacağı iddiası değildir.

## Etki

Silinen sütun veya kayıt iş akışlarını bozabilir. Yeni kod bir süre çalışsa bile eski istemci veya arka plan işi artık beklediği alanı bulamayabilir. Veriyi geri getirmek için yedek açmak gerekirken o arada oluşan yeni kayıtların korunması da ayrı sorun olur.

Onay sınırının bulunmaması, geniş yazma yetkisini kullanan otomasyona beklenenden fazla hareket alanı verir. CWE-862 eşlemesi bu örnekte yıkıcı işlemin yetkili onay olmadan yürütülmesine aittir. Her migration hatasının otomatik olarak aynı güvenlik açığı olduğu söylenmez. [Eksik yetkilendirme](https://cwe.mitre.org/data/definitions/862.html)

## Nasıl anlarsın

Migration dosyalarında `DROP`, `TRUNCATE` ve veri anlamını değiştiren yazmaları incele. Yalnız anahtar sözcük taramasıyla güvenli kararı verme. Bir yeniden adlandırma veya tip değişikliği de uygulama uyumluluğunu etkileyebilir. Çalıştırılan hedefi ve kullanılan kimliğin hangi işlerden erişilebilir olduğunu bul.

Yerel örnek gerçek SQLite üzerinde yıkıcı SQL çalıştırır. Güvenilir onay sağlayıcısı testte taklit edilir. Eksik, yanlış hedefli, değiştirilmiş, reddedilmiş veya süresi geçmiş onayda tabloya dokunulmadığı doğrulanır. Bu test onay sağlayıcısının kimlik doğrulamasını veya canlı bağlantının gerçekten belirtilen hedefe gittiğini kanıtlamaz.

## Nasıl düzeltirsin

1. **Değişikliği aşamalara ayır.** Uygun olduğunda önce yeni yapıyı ekle, veriyi taşı, uygulamayı geçir ve eski yapıyı sonra kaldır.
2. **Somut inceleme sun.** Hedef, SQL özeti, etkilenen veri ve geri dönüş planı aynı kayıtta bulunsun. Onaydan sonra içerik değişirse yeniden değerlendir.
3. **Kimliği sınırla.** Canlı migration yetkisi yalnız korunan yürütme yolunda kullanılabilsin. Genel geliştirme veya önizleme işi bu sırrı alamamalı.
4. **Dönüşü ayır.** Transaction içindeki hata geri alınabilir, tamamlanmış veri silme için ayrıca kurtarma gerekir. [Transaction sınırı](https://www.postgresql.org/docs/current/tutorial-transactions.html)
5. **Sonucu kontrol et.** İzinli değişikliğin çalıştığını, reddedilen değişikliğin veriyi değiştirmediğini ve uygulamanın yeni şemayı kullanabildiğini doğrula.

## Bir daha olmasın

Migration incelemesine veri kaybı ve hedef sorusunu ekle. Kod geri alma planını veri kurtarma planıyla aynı satırda tamamlandı sayma.

## Sınır

Boş ve yeniden üretilebilir geliştirme veritabanı aynı önem düzeyini gerektirmez. Örnekte onay servisi, kimlik doğrulama ve tek kullanımlık yürütme altyapısı kurulmaz. Bunlar gerçek dağıtımın güvenilir katmanından sağlanır. Onaylı migration tekrar çalıştırma, kilit, uzun süren veri taşıma ve geri yükleme sorunları ayrıca yönetilmelidir. Yerel test canlı sisteme komut göndermez.

## Düzeltme kodları

### Node.js: Hedefe bağlı değişiklik onayı

Önce:

```js
// migration/uygula.js, açıklama amaçlı. Yalnız geçici yerel veritabanında.
import { createHash } from 'node:crypto';
export async function applyMigration(db, sql, target, verifyApproval) {
  const digest = createHash('sha256').update(sql).digest('hex');
  // Özeti yazmak onay denetimi sağlamaz.
  db.exec('BEGIN');
  try {
    db.exec(sql);
    db.exec('COMMIT');
    return digest;
  } catch (error) {
    db.exec('ROLLBACK');
    throw error;
  }
}
// Commit sonrası transaction geri alma, silinen veriyi geri getirmez.
```

Sonra:

```js
// migration/uygula.js, açıklama amaçlı. Yalnız geçici yerel veritabanında.
import { createHash } from 'node:crypto';
export async function applyMigration(db, sql, target, verifyApproval) {
  const digest = createHash('sha256').update(sql).digest('hex');
  // Güvenilir onay servisi kimliği ve kaydın gerçekliğini ayrıca doğrular.
  const approval = await verifyApproval();
  if (!approval || approval.digest !== digest || approval.target !== target ||
      !Number.isFinite(approval.expiresAt) || approval.expiresAt <= Date.now() ||
      !approval.restoreDrillId || approval.decision !== 'approved') {
    throw new Error('Geçerli değişiklik onayı yok');
  }
  db.exec('BEGIN');
  try {
    db.exec(sql);
    db.exec('COMMIT');
    return digest;
  } catch (error) {
    db.exec('ROLLBACK');
    throw error;
  }
}
// verifyApproval istemciden gelen JSON'u doğrulamadan döndüren bir işlev olamaz.
// target, bu bağlantının güvenilir kurulumda belirlenen hedef kimliğidir.
```

Düzeltmeyi kanıtlayan test:

```js
// migration/uygula.test.mjs, açıklama amaçlı. Onay sahte, SQL gerçek SQLite.
import test from 'node:test';
import assert from 'node:assert/strict';
import { DatabaseSync } from 'node:sqlite';
import { createHash } from 'node:crypto';
const { applyMigration } = await import(process.env.ORNEK_DOSYA);
test('eksik veya uyuşmayan onay veriye dokunmaz', async () => {
  const db = new DatabaseSync(':memory:');
  try {
    db.exec('CREATE TABLE obsolete(id INTEGER); INSERT INTO obsolete VALUES(1)');
    const sql = 'DROP TABLE obsolete';
    const valid = { digest: createHash('sha256').update(sql).digest('hex'), target: 'test-db',
      expiresAt: Date.now() + 60000, restoreDrillId: 'local-drill', decision: 'approved' };
    for (const approval of [null, { ...valid, digest: 'changed' }, { ...valid, target: 'other' },
      { ...valid, expiresAt: 0 }, { ...valid, decision: 'rejected' }, { ...valid, restoreDrillId: '' }]) {
      await assert.rejects(applyMigration(db, sql, 'test-db', async () => approval));
      assert.equal(db.prepare('SELECT count(*) AS n FROM obsolete').get().n, 1);
    }
    await applyMigration(db, sql, 'test-db', async () => valid);
    assert.throws(() => db.prepare('SELECT * FROM obsolete').all(), /no such table/);
  } finally { db.close(); }
});
```

## Ajan kuralı (AGENTS.md)

```md
## Yıkıcı migration onaysız çalışıyor (vibecheck VC-098)
- Veri kaybettiren değişikliği çalıştırmadan önce incelet.
- Onayı hedef ve değişiklik özetiyle bağla.
- Canlı migration kimliğini onay sınırının dışında paylaşma.
- Geri yükleme yolunu ayrı ortamda dene.
- Önce ekle, taşı ve doğrula, eski alanı sonra kaldır.
```

## Kaynaklar

1. [Deployments and environments](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments), GitHub
2. [Reviewing deployments](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/review-deployments), GitHub
3. [PostgreSQL Transactions](https://www.postgresql.org/docs/current/tutorial-transactions.html), PostgreSQL
4. [CWE-862: Missing Authorization](https://cwe.mitre.org/data/definitions/862.html), MITRE
5. [Node.js SQLite parameter binding](https://nodejs.org/api/sqlite.html), Node.js

---

vibecheck · Komünite editörlüğü. Metin CC BY 4.0, prompt ve kural parçaları MIT-0. Kaynak: https://vibecheck.komunite.com.tr/madde/geri-alinamaz-migration-onaysiz-calisiyor
