Yapay Zeka / AI

Cloudflare Security Audit Skill: Avcı Bulur, Şeytanın Avukatı Çürütür

Cloudflare'in açık kaynak security-audit skill'i kodlama ajanını altı aşamalı bir güvenlik denetçisine çeviriyor; en değerli kuralı, bulguyu bulan ajanın onu doğrulayamaması. Kurulum, zayıf yanlar ve findings.json'u CI kapısına bağlayan bir PHP betiği.

9 dk okuma

Bu yazının İngilizcesi de var →
Ahşap kaplı duruşma odasında masada kırmızı mühürlü dosya; bir yanda fener ve büyüteçli bulgu kartları, karşıda boş avukat koltuğu ve üç tepsi: güvenlik denetiminde bulguyu bulan ile doğrulayanın ayrılması.

Salı günü yayınlanan Vibe Coder #5.3'te dört ekosistemin model tabanlı güvenlik denetçilerini tabloya koyup hepsine aynı etiketi yapıştırmıştık: göz. Kilit değil, göz. Peki gözün gördüğünü kim sorguluyor? Model bir açık bulduğunu söylediğinde, o açığın gerçek olduğuna yine aynı model mi karar veriyor?

Bu hafta radarda bu soruya bir şirketin kendi sahasından verdiği cevap var: Cloudflare security audit skill'i. Kodlama ajanınıza kurduğunuz, onu altı aşamalı bir güvenlik denetçisine çeviren bir talimat paketi. Paketin en değerli satırı bir saldırı listesi değil, bir usul kuralı: bulgunun önce bir avukatı olur ve o avukat bulguyu çürütmeye çalışır.

Metaforumuz da buradan: şeytanın avukatı. Avcı bulur, avukat çürütmeye çalışır, çürütülemeyen dosya ancak o zaman hükme gider.

Cloudflare security audit skill nedir?#

Tek cümleyle: kodlama ajanını, birbirinden yalıtılmış alt ajanlarla keşif, avlanma, doğrulama, yapılandırılmış çıktı, bağımsız kayıt doğrulaması ve rapor aşamalarından geçiren, MIT lisanslı bir Agent Skill.

Depo cloudflare/security-audit-skill. Commit kayıtlarında başlıca yazar Dan Jones (literally-dan); Cloudflare blogundaki yazar sayfasında unvanı Principal Red Team Engineer. Skill, Dan Jones, Alexandra Godoi ve Grant Bourzikas'ın 18 Haziran 2026'da yayımladığı Build your own vulnerability harness yazısının çıkış noktası. README'nin ifadesiyle Cloudflare'in filo çapındaki zafiyet bulma sistemi bu tek depoluk skill'den büyümüş. Sistemin kendisi açık değil; yazı "harness'ın kendisi de umarız yakında gelir" diyor.

Kurulum#

Kurulum Vercel'in Skills CLI'ı (MIT) üzerinden. Aynı komut -a ile farklı ajanlara kurulabiliyor:

Bash
# Claude Code (.claude/skills/)
npx skills add https://github.com/cloudflare/security-audit-skill --skill security-audit -a claude-code
​
# Codex, Cursor, OpenCode, Gemini CLI ve Antigravity proje içinde .agents/skills/ altını kullanır
npx skills add https://github.com/cloudflare/security-audit-skill --skill security-audit -a codex
​
# kullanıcı düzeyinde kurulum
npx skills add https://github.com/cloudflare/security-audit-skill --skill security-audit --global

Sonra ajana "security audit this codebase" demeniz yetiyor. Skill iki modda çalışıyor: güvenlik sorusu ya da odaklı inceleme "guidance" modunda kalıyor, dosya yazmıyor. Açıkça tam denetim isterseniz altı aşama çalışıyor ve çıktı varsayılan olarak deponun dışına, ~/security-audit-skill/<repo-adı>/run-<N> altına yazılıyor.

Baştan söyleyeyim: bu yazı için skill'i bir ajana kurup denetim koşturmadım, Node betiklerini de çalıştırmadım. 14 Eylül 2026 tarihli c1c8a8c commit'indeki kaynağı satır satır okudum. Aşağıdaki PHP kapısını ise kendim yazıp çalıştırdım.

Avcı bulur, avukat çürütür#

Altı aşamanın omurgası tek kural: bulguyu bulan ajan onu doğrulayamaz. Üçüncü aşamada her aday, onu hiç görmemiş taze bir ajana gidiyor ve o ajanın talimatı şu cümleyle başlıyor:

You did not write this candidate. Try to refute it from repository source and bounded local evidence.

Doğrulayıcıya başka bir doğrulayıcının vardığı sonuç da verilmiyor. Beşinci aşamada son kayıtlar bir kez daha, yine taze ajanlarca kaynağa karşı kontrol ediliyor.

Bu kuralın neden orada olduğunu harness yazısı tek cümleyle anlatıyor: "Avcının kendi ödevini notlamasına izin verirseniz, ürettiği her şeyi güvenle onaylar." Avukatın işi de ölçülüyor: keşif aşamasından avcılara daha iyi bağlam aktarılınca ilk doğrulamada reddedilen aday oranı %40'tan %11'e düşmüş. Avukat ayrı durduğu için bu iyileşme ölçülebilmiş; avcı kendini notlasaydı ret oranı hiç oluşmazdı.

Aynı yazıda 5.3'teki iz sürücümüze dokunan bir itiraf da var: "Semgrep'i sonuna kadar bağladık, avcılar bir aylık koşularda onu sıfır kez çağırdı." En çok kullanılan araç ise ajanların "şu ortam olsa doğrulardım" diye not düştüğü dilek listesi olmuş; 128 depoda 25.472 kez yazılmış.

Gizli Hazine: skill'in çıktısı üç hükümden biri. confirmed için tam kaynak izi ve sınırlı yerel bir gözlem şart; derece (overall_severity) yalnızca bu hükümde var. needs_validation "düşük güvenli bulgu" değil: kaynağa dayanan ama karar verdirecek bir gerçeği depoda ya da sandbox'ta görülemeyen aday. Derecesi yok, engeli (blockers) yazılı. rejected ise çürütülmüş aday ve gerekçesi. Şema bunu zorluyor; needs_validation kaydına derece koyamıyorsunuz.

findings.json'u kapıya bağlamak#

5.3'ün dersini hatırlayın: model denetçi göz, kilit çıkış koduyla duran bir komut. Skill'in çıktısı makinenin okuyabileceği bir JSON olduğu için kilidi kurmak kolay. Aşağıdaki betik findings.json'u okuyor, eşik üstü doğrulanmış bulgu varsa 2 ile çıkıyor, doğrulama bekleyenleri iş olarak listeliyor.

İkinci kontrol daha önemli. Boş bir findings.json temiz kod demek değil; koşu bütçe bitince yarıda kalmış olabilir. Skill bunu run-metadata.json içindeki run_status alanına yazıyor ve yalnızca her aday bağımsız bir son karar aldıysa complete diyor. Kapı complete görmeden açılmıyor.

PHP
<?php
​
declare(strict_types=1);
​
/**
 * Denetim kapısı: security-audit skill'inin findings.json çıktısını okur.
 *   php denetim-kapisi.php <findings.json> [esik]   esik: low|medium|high|critical (varsayılan high)
 * Çıkış: 0 kapı açık, 2 eşik üstü doğrulanmış bulgu var ya da koşu tamamlanmamış, 1 dosya/biçim hatası.
 * needs_validation kaydı kapıyı kapatmaz ama sahibine iş olarak listelenir.
 */
​
const SIRA = ['informational' => 0, 'low' => 1, 'medium' => 2, 'high' => 3, 'critical' => 4];
​
$dosya = $argv[1] ?? '';
$esik = $argv[2] ?? 'high';
if ($dosya === '' || !is_file($dosya) || !isset(SIRA[$esik])) {
    fwrite(STDERR, "Kullanım: php denetim-kapisi.php <findings.json> [low|medium|high|critical]\n");
    exit(1);
}
​
try {
    $kayitlar = json_decode((string) file_get_contents($dosya), true, 64, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    fwrite(STDERR, "findings.json okunamadı: {$e->getMessage()}\n");
    exit(1);
}
if (!is_array($kayitlar) || !array_is_list($kayitlar)) {
    fwrite(STDERR, "findings.json bir dizi olmalı.\n");
    exit(1);
}
​
// Boş liste "temiz" demek değil: koşu yarıda kaldıysa bulgu sayısı bir şey söylemez.
$meta = dirname($dosya) . '/run-metadata.json';
$durum = is_file($meta) ? (json_decode((string) file_get_contents($meta), true)['run_status'] ?? '?') : 'dosya yok';
if ($durum !== 'complete') {
    fwrite(STDERR, "run-metadata.json run_status: $durum. Yarım koşuya kapı açılmaz.\n");
    exit(2);
}
​
$sayac = ['confirmed' => 0, 'needs_validation' => 0, 'rejected' => 0];
$kapatan = [];
$bekleyen = [];
​
foreach ($kayitlar as $k) {
    $karar = $k['verdict'] ?? '';
    if (!isset($sayac[$karar])) {
        fwrite(STDERR, "Bilinmeyen verdict: " . var_export($karar, true) . "\n");
        exit(1);
    }
    $sayac[$karar]++;
    $iz = is_array($k['trace'] ?? null) ? $k['trace'] : [];
    $son = end($iz);
    $yer = is_array($son) ? "{$son['file']}:{$son['line']}" : '?';
​
    if ($karar === 'confirmed') {
        $derece = $k['severity']['overall_severity'] ?? '';
        if (!isset(SIRA[$derece])) {
            fwrite(STDERR, "{$k['fingerprint']}: doğrulanmış kayıtta derece yok.\n");
            exit(1);
        }
        if (SIRA[$derece] >= SIRA[$esik]) {
            $kapatan[] = "[$derece] {$k['title']} ($yer)";
        }
    } elseif ($karar === 'needs_validation') {
        $bekleyen[] = "{$k['title']} ($yer) — " . implode('; ', $k['blockers'] ?? []);
    }
}
​
printf("confirmed %d, needs_validation %d, rejected %d\n", $sayac['confirmed'], $sayac['needs_validation'], $sayac['rejected']);
foreach ($bekleyen as $satir) {
    echo "Doğrulama bekliyor: $satir\n";
}
if ($kapatan !== []) {
    foreach ($kapatan as $satir) {
        fwrite(STDERR, "Eşik ($esik) üstü: $satir\n");
    }
    exit(2);
}
​
echo "Eşik ($esik) üstü doğrulanmış bulgu yok.\n";
exit(0);

Şemadaki alan adlarıyla elle yazdığım üç kayıtlık bir örnekle (bir doğrulanmış, bir bekleyen, bir reddedilmiş; şemanın bütün alanlarını taşımıyor) ve iki sahte koşu klasörüyle denedim (PHP 8.3.6 CLI, 10.11.2026):

Bash
$ php denetim-kapisi.php kosu-1/findings.json
confirmed 1, needs_validation 1, rejected 1
Doğrulama bekliyor: Yükleme klasöründe PHP çalışıyor olabilir (src/Yukleme.php:27) — Apache'nin uploads/ için PHP çalıştırıp çalıştırmadığı depoda yok
Eşik (high) üstü: [high] Sipariş detayı başka müşterinin siparişini gösteriyor (src/SiparisDeposu.php:41)
$ echo $?
2
$ php denetim-kapisi.php kosu-2/findings.json
run-metadata.json run_status: incomplete. Yarım koşuya kapı açılmaz.
$ echo $?
2

İkinci koşunun findings.json'u boş bir dizi. run_status kontrolü olmasa kapı 0 ile açılacaktı. Eşiği critical yapınca birinci koşu da 0 dönüyor. Bu kapıyı her commit'te değil, gece ya da sürüm öncesi çalışan işe koymak daha mantıklı; boru hattı tarafını serinin 6.1 bölümünde konuşacağız.

Ne zaman işe yaramaz?#

  • Tek koşu yarım iş. README'nin kendi cümlesi: test koşularında tek koşu, tekrarlanan koşuların toplamda bulduğu açıkların kabaca yarısını bulmuş. Harness yazısı da açıkça söylüyor: sistem için bir yanlış negatif oranı iddia etmiyorlar.
  • Ucuz değil. Tam denetim dört keşif ajanıyla başlıyor, her dalgadan sonra bir kapsam eleştirmeni, her aday için bir ya da iki doğrulayıcı geliyor. Skill paralel alt ajan çalıştırabilen bir ajan istiyor. Bütçeyi ajan çağrısı sayısıyla sınırlayabiliyorsunuz; dolar tahmini yok.
  • Sandbox yoksa her şey "bekliyor" kalır. Hedef kodu çalıştırmak için ağsız, boş ortam değişkenli, yalnızca scratch klasörüne yazabilen, kaynak sınırlı bir OS sandbox'ı şart. Bu yoksa skill kod çalıştırmıyor, adayı needs_validation olarak bırakıyor. Bu kontrolleri kurmamış bir makinede, çalıştırılarak doğrulanması gereken her aday o sütunda kalır.
  • PHP'ye özel bölüm yok. Saldırı sınıfları dil bağımsız; on bir ek dosyada PHP, Composer ya da herhangi bir PHP çerçevesi geçmiyor.
  • Sürüm yok. Depoda tek bir release ya da etiket yok; npx skills add deponun o anki varsayılan dalını kuruyor. Hangi commit'i kurduğunuzu not edin.
  • Kurduğunuz şey yalnızca Markdown değil. Pakette Node ile çalışan iki doğrulayıcı betik var. Skill kurmadan önce ne yapılacağını SkillSpector yazısında konuşmuştuk; bu paket için de geçerli.

Bakım durumu (10.11.2026)#

Lisans MIT, telif Cloudflare, Inc.
İlk commit 18.06.2026
Son commit 14.09.2026 (c1c8a8c), ana dalda 14 commit
Sürüm Yok (release/etiket yok)
İlgi 63,9 bin yıldız, 4,1 bin fork, 42 açık konu, 73 PR
Katkı Dan Jones dışında üç topluluk katkısı: bellek güvenliği sınıfları, iz sırası doğrulaması, npx skills add uyumluluğu
İletişim security-ai-research@cloudflare.com (README)

Alternatifler#

Araç Arkasında Yaklaşım
security-audit skill Cloudflare, MIT Altı aşamalı tam denetim; bulguyu bulmayan taze bir ajan doğrular; çıktı şemalı JSON
Claude Code /security-review Anthropic Dalınızla varsayılan dal arasındaki farkı injection, yetki ve veri sızıntısı açısından inceler
Codex Security OpenAI Depoya özel tehdit modeli, mümkünse sandbox'ta doğrulama, yama önerisi; bulut sürümü araştırma önizlemesinde
Gemini CLI security eklentisi gemini-cli-extensions, Apache-2.0 /security:analyze ile yerel diff incelemesi, /security:scan-deps ile OSV-Scanner
Semgrep CE Semgrep, Inc., LGPL-2.1 Kural tabanlı tarama; fonksiyonlar arası taint Pro'da

Diğer dört aracın özellikleri 08.11.2026'da resmi dokümanlarından okunmuştu, bağlantılar 5.3'te. Ayrım şu: onlar farkı ya da depoyu hızlıca inceleyen bir göz. Cloudflare'inki pahalı, yavaş ve tekrar tekrar koşturulmak üzere tasarlanmış bir usul.

Bizim tarafta nerede kullanırım?#

Her commit'te değil. Ajanın oturumunda 5.3'teki mayın kapısı duruyor; bu skill'in yeri büyük bir değişikliğin öncesi. Multi-storefront yazısındaki gibi tek veritabanında birden çok vitrinin durduğu bir yapıda, yalnızca veri yalıtımı ek dosyasını seçen, kapsamı sınırlı bir koşu en çok işe yarayacak yer. O dosyadaki temel kuralların ilki, o yazının tezinin neredeyse aynısı: kayıttaki kiracı ya da sahip alanı yalıtım değildir; yalıtımı her okuma ve yazma yolundaki sorgu, anahtar ya da politika sağlar.

Bir uyarı daha: skill hiçbir zaman canlı ortamı yoklamıyor. Canlıdaki Apache ayarı, proxy davranışı ya da sağlayıcı yapılandırması depoda yoksa hüküm needs_validation. Araç bilmediğini bildiğini söylüyor; o sütunu kapatmak ise size düşüyor.

Avcının hızı etkileyici, ama bu paketi değerli kılan avukat. Kendi ödevini notlayan ajana not verdirmeyin. Teknolojiyle kalın.

Yorumlar

0 yorum

Yorum yazın

kişisine yanıt

Yorumlar onaylandıktan sonra görünür. E-posta adresiniz kimseyle paylaşılmaz. Kişisel verileriniz nasıl işlenir?