8.5 KiB
8.5 KiB
Trip Create Security Tests
Güvenlik Özellikleri
1. ✅ Zorunlu Kimlik Doğrulama (Auth Mandatory)
Özellik: Anonim kullanıcılar seyahat oluşturamaz.
Test Senaryoları:
// ❌ BAŞARISIZ: Giriş yapmadan seyahat oluşturma
await supabase.auth.signOut();
const result = await tripsApiSafe.create({
title: "Test Seyahati"
});
// Beklenen: Error: "Seyahat oluşturmak için giriş yapmalısınız."
// ✅ BAŞARILI: Giriş yaparak seyahat oluşturma
await supabase.auth.signInWithPassword({
email: "test@example.com",
password: "password123"
});
const result = await tripsApiSafe.create({
title: "Test Seyahati"
});
// Beklenen: Trip objesi döner
2. ✅ Rate Limiting (5 trip/saat)
Özellik: Kullanıcı saatte en fazla 5 seyahat oluşturabilir.
Test Senaryoları:
// ✅ İlk 5 seyahat başarılı
for (let i = 1; i <= 5; i++) {
const result = await tripsApiSafe.create({
title: `Seyahat ${i}`
});
console.log(`✅ Seyahat ${i} oluşturuldu`);
}
// ❌ 6. seyahat başarısız
try {
await tripsApiSafe.create({
title: "Seyahat 6"
});
} catch (error) {
console.log(error.message);
// Beklenen: "Saatlik limit aşıldı. X dakika sonra tekrar deneyin."
}
// ⏰ 1 saat sonra tekrar dene
setTimeout(async () => {
const result = await tripsApiSafe.create({
title: "Yeni Seyahat"
});
console.log("✅ Limit sıfırlandı, yeni seyahat oluşturuldu");
}, 3600000);
3. ✅ Input Validation
Özellik: Tüm girişler doğrulanır ve sanitize edilir.
3.1 Başlık Validasyonu
// ❌ Başlık çok kısa (min: 3 karakter)
await tripsApiSafe.create({ title: "AB" });
// Beklenen: Error: "Seyahat başlığı en az 3 karakter olmalıdır."
// ❌ Başlık çok uzun (max: 200 karakter)
await tripsApiSafe.create({
title: "A".repeat(201)
});
// Beklenen: Error: "Seyahat başlığı en fazla 200 karakter olabilir."
// ❌ Başlık boş
await tripsApiSafe.create({ title: "" });
// Beklenen: Error: "Seyahat başlığı alanı zorunludur."
// ✅ Geçerli başlık
await tripsApiSafe.create({
title: "İstanbul Gezisi"
});
3.2 Açıklama Validasyonu
// ❌ Açıklama çok uzun (max: 2000 karakter)
await tripsApiSafe.create({
title: "Test",
description: "A".repeat(2001)
});
// Beklenen: Error: "Açıklama en fazla 2000 karakter olabilir."
// ✅ Geçerli açıklama
await tripsApiSafe.create({
title: "Test",
description: "Harika bir seyahat planı"
});
3.3 Tarih Validasyonu
// ❌ Bitiş tarihi başlangıçtan önce
await tripsApiSafe.create({
title: "Test",
start_date: "2026-03-01",
end_date: "2026-02-01"
});
// Beklenen: Error: "Bitiş tarihi başlangıç tarihinden önce olamaz."
// ❌ Geçersiz tarih formatı
await tripsApiSafe.create({
title: "Test",
start_date: "invalid-date"
});
// Beklenen: Error: "Başlangıç tarihi geçersiz."
// ❌ Çok uzun seyahat (max: 365 gün)
await tripsApiSafe.create({
title: "Test",
start_date: "2026-01-01",
end_date: "2027-02-01"
});
// Beklenen: Error: "Seyahat süresi en fazla 365 gün olabilir."
// ✅ Geçerli tarihler
await tripsApiSafe.create({
title: "Test",
start_date: "2026-03-01",
end_date: "2026-03-10"
});
3.4 Konum Validasyonu
// ❌ Geçersiz enlem (lat: -90 ile 90 arası)
await tripsApiSafe.create({
title: "Test",
start_lat: 100,
start_lng: 30
});
// Beklenen: Error: "Enlem değeri -90 ile 90 arasında olmalıdır."
// ❌ Geçersiz boylam (lng: -180 ile 180 arası)
await tripsApiSafe.create({
title: "Test",
start_lat: 40,
start_lng: 200
});
// Beklenen: Error: "Boylam değeri -180 ile 180 arasında olmalıdır."
// ❌ Konum tipi sayı değil
await tripsApiSafe.create({
title: "Test",
start_lat: "invalid",
start_lng: 30
});
// Beklenen: Error: "Konum bilgileri geçersiz."
// ✅ Geçerli konum
await tripsApiSafe.create({
title: "Test",
start_lat: 41.0082,
start_lng: 28.9784
});
3.5 İlgi Alanları Validasyonu
// ❌ Çok fazla ilgi alanı (max: 20)
await tripsApiSafe.create({
title: "Test",
interests: Array(21).fill("test")
});
// Beklenen: Error: "En fazla 20 ilgi alanı seçebilirsiniz."
// ❌ Geçersiz ilgi alanı formatı
await tripsApiSafe.create({
title: "Test",
interests: ["A".repeat(51)]
});
// Beklenen: Error: "İlgi alanı formatı geçersiz."
// ✅ Geçerli ilgi alanları
await tripsApiSafe.create({
title: "Test",
interests: ["history", "culture", "food"]
});
4. ✅ Meaningful Error Messages
Özellik: Kullanıcıya anlamlı hata mesajları gösterilir.
Hata Mesajları:
- ✅ "Seyahat oluşturmak için giriş yapmalısınız." (Auth hatası)
- ✅ "Saatlik limit aşıldı. X dakika sonra tekrar deneyin." (Rate limit)
- ✅ "Seyahat başlığı en az 3 karakter olmalıdır." (Validation)
- ✅ "Bitiş tarihi başlangıç tarihinden önce olamaz." (Validation)
- ✅ "Seyahat oluşturulurken bir hata oluştu. Lütfen tekrar deneyin." (DB hatası)
5. ✅ Security Audit Logging
Özellik: Tüm güvenlik olayları loglanır.
Log Formatı:
[SECURITY AUDIT] 2026-02-08T10:30:00.000Z | Event: TRIP_CREATE_SUCCESS | User: user-uuid | Details: { tripId, title, destination }
Log Olayları:
TRIP_CREATE_FAILED- Yetkisiz erişim denemesiTRIP_CREATE_RATE_LIMITED- Rate limit aşımıTRIP_CREATE_VALIDATION_FAILED- Geçersiz inputTRIP_CREATE_DB_ERROR- Veritabanı hatasıTRIP_CREATE_SUCCESS- Başarılı oluşturma
Test:
// Console'u izle
console.log = jest.fn();
// Başarısız deneme
try {
await supabase.auth.signOut();
await tripsApiSafe.create({ title: "Test" });
} catch (error) {
// Log kontrolü
expect(console.log).toHaveBeenCalledWith(
expect.stringContaining('[SECURITY AUDIT]'),
expect.stringContaining('TRIP_CREATE_FAILED'),
expect.stringContaining('Unauthorized')
);
}
Kullanım Örnekleri
Frontend'de Güvenli Kullanım
import { tripsApiSafe } from '@/db/api';
import { toast } from 'sonner';
// ✅ DOĞRU: tripsApiSafe kullan
const handleCreateTrip = async (formData) => {
try {
const trip = await tripsApiSafe.create({
title: formData.title,
description: formData.description,
start_date: formData.startDate,
end_date: formData.endDate,
destination: formData.destination,
interests: formData.interests
});
toast.success('Seyahat başarıyla oluşturuldu!');
return trip;
} catch (error) {
// Kullanıcıya anlamlı hata mesajı göster
toast.error(error.message);
console.error('Trip creation error:', error);
}
};
// ❌ YANLIŞ: tripsApi kullanma (güvensiz)
const handleCreateTripUnsafe = async (formData) => {
const trip = await tripsApi.create(formData); // Validation yok!
};
Performans Notları
Rate Limiter Memory Management
Rate limiter in-memory Map kullanır. Production'da Redis kullanılması önerilir:
// Gelecek iyileştirme: Redis ile rate limiting
import { Redis } from '@upstash/redis';
const redis = new Redis({
url: process.env.REDIS_URL,
token: process.env.REDIS_TOKEN
});
const checkRateLimitRedis = async (userId: string) => {
const key = `rate_limit:trip_create:${userId}`;
const count = await redis.incr(key);
if (count === 1) {
await redis.expire(key, 3600); // 1 saat
}
if (count > 5) {
const ttl = await redis.ttl(key);
throw new Error(`Saatlik limit aşıldı. ${Math.ceil(ttl / 60)} dakika sonra tekrar deneyin.`);
}
};
Güvenlik Checklist
- ✅ Auth mandatory - Anonim kullanıcılar seyahat oluşturamaz
- ✅ Rate limiting - 5 trip/saat per user
- ✅ Input validation - Tüm alanlar doğrulanır
- ✅ Meaningful errors - Kullanıcıya anlamlı mesajlar
- ✅ Security logging - Tüm olaylar loglanır
- ✅ SQL injection koruması - Input sanitization
- ✅ XSS koruması - String validation
- ✅ Data integrity - Tarih ve konum validasyonu
Sonuç
tripsApiSafe.create() fonksiyonu artık production-ready ve güvenli:
- Auth Mandatory: Sadece giriş yapmış kullanıcılar seyahat oluşturabilir
- Rate Limiting: Spam ve abuse koruması
- Input Validation: Tüm girişler doğrulanır ve sanitize edilir
- Error Handling: Kullanıcıya anlamlı mesajlar
- Audit Trail: Güvenlik olayları loglanır
Kullanım: Frontend'de her zaman tripsApiSafe.create() kullanın, tripsApi.create() kullanmayın!