1. Giriş
Bu codelab'de, önceki iki codelab'i bir araya getireceksiniz. Antigravity'ye bir özellik isteği göndereceksiniz. Ardından, Antigravity'nin tehdit modeliyle planlama yapmasını, başarısız testler yazmasını, özelliği uygulamasını ve göndermesini izleyeceksiniz. Push işlemi sırasında, önceki codelab'deki güvenlik kapısı, değiştirilen dosyalarda CodeMender'ı çalıştırır. Örnek uygulamada, temsilcinin düzenlediği dosyada hâlâ açık bir komut yerleştirme olduğu için ilk push işlemi engellenir. Temsilcinin, önce test ederek bulguyu temizlediğini ve tekrar push işlemi yaptığını görürsünüz. Bu süreçte CodeMender'dan herhangi bir insan istemi gelmez.
Öğrenecekleriniz
- CodeMender'ı başsız olarak çalıştırma. Bu sayede ne kanca ne de temsilci
[Y/n]istemlerinde engellenir. - Temsilci bir özellik oluşturduğunda Planla → Kırmızı → Yeşil → Yeniden düzenle döngüsünün ürettiği sonuçlar:
threat_model.md, testler, kod ve bir commit. - Güvenlik kapısının, gönderilen bir dosyada açık bulgular olduğunda nasıl yanıt verdiği ve temsilcinin bunları temizlemek için
security-test-writervedefensive-developerbecerilerini nasıl kullandığı. - Kapının geride bıraktığı denetim takibini okuma:
.security-gate/findings-log.ndjson,CONTEXT.md'deki otomatik olarak geliştirilen kurallar ve git geçmişi.
Ön koşullar
- Önceki iki codelab'den
~/codemender-labçalışma alanı vemaindalında temiz bir git çalışma ağacı. - Antigravity yüklendi.
~/codemender-lab, klasör olarak açılabilir. git,jqve Flask'ın yüklü olduğu Python sanal ortamı.
2. Önceki Codelab'lerde Yapılan Kurulumu Doğrulama
1. adım: Laboratuvar dizinine dönün
cd ~/codemender-lab && source .venv/bin/activate
2. adım: Çerçeve dosyalarının yerinde olduğunu onaylayın
ls .agents/hooks.json .agents/security_gate_hook.sh .agents/rules/secure_tdd_workflow.md
ls .agents/skills
ls tests
git status --short && git branch --show-current
Altı beceri dizini (defensive-developer, history-context-seeder, local-refactor-scanner, security-test-writer, skill-evolution-updater, threat-model-assessor), tests/__init__.py ve tests/test_data_service.py, boş bir git status ve main görmeniz gerekir.
Herhangi bir değişiklik olmadığını doğrulamak için çevrimdışı kanca testlerini bir kez daha çalıştırın:
bash .agents/tests/run_tests.sh | tail -1
60 passed, 0 failed
3. adım: CodeMender ortamını onaylayın
cm init --verify
CodeMender CLI Basics'te yapılandırıldığı gibi, VCS türü olarak git ve derleme komutu olarak python3 -m compileall -q . bildirilmelidir.
4. adım: Eski bulguları temizleyin
cm clean
İstendiğinde y yazın. Bu işlem ~/.codemender/state.db öğeyi boşaltır.
5. adım: Push işlemi için uzak depo oluşturun
Kanca, çalışma ağacınızı origin/main ile karşılaştırır ve git push'in gidecek bir yere ihtiyacı vardır. Aynı makinedeki boş bir depo yeterlidir:
git init --bare ~/codemender-remote.git
git remote add origin ~/codemender-remote.git
git push -u origin main
Bu push işlemi kendi terminalinizde gerçekleştiğinden Antigravity kancası bunu görmez. Bu noktadan sonra, aracı push işlemlerini yapar.
3. CodeMender'ı başsız çalıştırma
1. adım: Onay istemlerini devre dışı bırakın
CodeMender, varsayılan olarak çalıştırdığı her komuttan ve yazdığı her dosyadan önce [Y/n] sorar. Kancanın içinde yanıt verecek kimse yoktur. ~/.codemender/config.yaml sayfasını açıp her iki tools işaretini de false olarak ayarlayın veya şu komutu çalıştırın:
sed -i.bak 's/^ confirm_commands: true/ confirm_commands: false/; s/^ confirm_writes: true/ confirm_writes: false/' ~/.codemender/config.yaml
rm ~/.codemender/config.yaml.bak
Elde edilen bölüm şu şekilde görünmelidir:
tools:
confirm_commands: false
confirm_writes: false
2. adım: Doğrulayın
cm init --verify
4. Özellik İsteği
1. adım: Çalışma alanını açın
~/codemender-lab dosyasını Antigravity'de klasör olarak açın ve yeni bir sohbet başlatın. Her zaman etkin olan secure_tdd_workflow kuralı artık her sıranın bir parçasıdır ve altı beceri kullanılabilir.
2. adım: Temsilciye görevi verin
Aşağıdaki istemi yapıştırın. Güvenlikten hiç bahsedilmediğine dikkat edin. Temsilciyi tehdit modelleme, test öncelikli geliştirme ve savunma kalıplarına yönlendirmek istemin değil, kural ve becerilerin görevidir:
sample_app/data_service.py hizmetine, kullanıcıların e-posta adreslerini güncellemelerine olanak tanıyan bir POST /update_profile uç noktası ekleyin. Form alanları olarak user_id ve email değerlerini kabul eder. Değişiklik tamamlandığında ve test paketi başarılı olduğunda değişikliği kaydedip origin main adresine gönderin.
3. adım: Temsilcinin komutlarını onaylayın
Kanca, araç izni sisteminin dışında çalışır ancak temsilcinin kendi komutları çalışmaz. python3 -m unittest ..., git add/commit/push ve çalıştırmaya karar verdiği tüm cm komutları için istemler görebilirsiniz. Bunları onaylayın. Antigravity, kararı hatırlamayı teklif ederse kabul edin. Böylece bu codelab'in ilerleyen bölümlerindeki düzeltme döngüsü, tekrarlanan istemlerde takılmaz.
5. Temsilcinin yaptığı işlemler: Planlama, Kırmızı, Yeşil, Yeniden Düzenleme
Aşağıda örnek bir çalıştırma gösterilmektedir. Temsilcinizin kullandığı ifadeler, dosya adları ve kodlar farklı olabilir. Bu normaldir. Değişmemesi gereken şey sıradır: testlerden önce tehdit modeli, koddan önce başarısız testler, kaydetmeden önce yeşil paket.
A aşaması: Planlama ve tehdit modelleme
Temsilci, çalışma alanı kökünde CONTEXT.md dosyasını okur, threat-model-assessor dosyasını yükler ve threat_model.md dosyasını yazar:
# Feature Plan & Threat Model: Update Profile Email
## 1. Functional Scope & Requirements
- **Goal**: Allow a user to change the email address stored for their account.
- **Deliverables**: `POST /update_profile` in `sample_app/data_service.py`; tests in `tests/test_update_profile.py`.
- **Functional Acceptance Criteria**: Valid `user_id` + `email` returns 200 and the row is updated; unknown `user_id` returns 404.
## 2. Entry Points & Data Inputs
- Endpoint / Input: `POST /update_profile`, form fields `user_id`, `email`
- Format & Constraints: `user_id` positive integer; `email` <= 254 chars, single `@`, non-empty local and domain parts.
## 3. Trust Boundaries & Access Controls
- Authentication: None exists in this sample. Recorded as a known gap; out of scope for this change.
- Authorization Role: N/A (see above).
## 4. STRIDE Threat Matrix & Security Acceptance Criteria
- Tampering: SQL injection through `user_id`/`email` -> parameterized `UPDATE`; test that `"1 OR 1=1"` returns 400.
- Tampering: Malformed email stored -> strict validation; test that `"not-an-email"` returns 400.
- Information Disclosure: Stack traces on bad input -> return JSON error bodies only.
- Denial of Service: Oversized email -> length cap at 254; test returns 400.
B aşaması: Başarısız testler (KIRMIZI)
Temsilci security-test-writer yükler ve tests/test_update_profile.py oluşturur. Tipik bir sürüm, çalışma dizininde tek kullanımlık bir users.db kullanır. Bu nedenle, önceki codelab users.db dosyasını .gitignore dosyasına ekledi:
import os
import sqlite3
import unittest
from sample_app.data_service import app
class UpdateProfileTests(unittest.TestCase):
def setUp(self):
conn = sqlite3.connect('users.db')
conn.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username TEXT, email TEXT)")
conn.execute("INSERT OR REPLACE INTO users (id, username, email) VALUES (1, 'alice', 'old@example.com')")
conn.commit()
conn.close()
self.client = app.test_client()
def tearDown(self):
os.remove('users.db')
def test_updates_email_for_valid_request(self):
r = self.client.post('/update_profile', data={'user_id': '1', 'email': 'new@example.com'})
self.assertEqual(r.status_code, 200)
def test_rejects_non_numeric_user_id(self):
r = self.client.post('/update_profile', data={'user_id': '1 OR 1=1', 'email': 'new@example.com'})
self.assertEqual(r.status_code, 400)
def test_rejects_malformed_email(self):
r = self.client.post('/update_profile', data={'user_id': '1', 'email': 'not-an-email'})
self.assertEqual(r.status_code, 400)
def test_rejects_oversized_email(self):
r = self.client.post('/update_profile', data={'user_id': '1', 'email': 'a' * 250 + '@x.io'})
self.assertEqual(r.status_code, 400)
Temsilci, python3 -m unittest discover -s tests komutunu çalıştırır ve rota henüz mevcut olmadığından yeni testlerin 404 != 200 ve 404 != 400 ile başarısız olduğunu onaylar. Başarısız olmak için doğru neden budur. İçe aktarma hatası veya yazım hatasından kaynaklanan bir başarısızlık KIRMIZI değildir; bozuk bir testtir.
C aşaması: Minimum uygulama (YEŞİL)
Temsilci, defensive-developer sayfasını yükler ve uç noktayı ekler. CONTEXT.md, SQL için gerekli kalıp olarak bunu belirttiğinden parametreleştirilmiş bir sorgu kullanır:
import re
EMAIL_PATTERN = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
@app.route('/update_profile', methods=['POST'])
def update_profile():
user_id = request.form.get('user_id', '')
email = request.form.get('email', '')
if not user_id.isdigit():
return jsonify({"error": "user_id must be a positive integer"}), 400
if len(email) > 254 or not EMAIL_PATTERN.match(email):
return jsonify({"error": "invalid email address"}), 400
conn = sqlite3.connect('users.db')
try:
cursor = conn.execute("UPDATE users SET email = ? WHERE id = ?", (email, int(user_id)))
conn.commit()
finally:
conn.close()
if cursor.rowcount == 0:
return jsonify({"error": "user not found"}), 404
return jsonify({"status": "updated", "user_id": int(user_id)}), 200
Paket artık başarılı: dört yeni test ve önceki codelab'deki karakterizasyon testi.
D aşaması: Yeniden düzenleme, tarama, kaydetme
Temsilci local-refactor-scanner dosyasını yükler, git diff dosyasını inceler, tüm paketi yeniden çalıştırır ve kaydeder:
git add sample_app/data_service.py tests/test_update_profile.py threat_model.md
git commit -m "feat: add POST /update_profile with validated input and parameterized update"
6. git push'ta güvenlik kapısı
İlk push reddedilir.
Aracı, git push origin main çalıştırılır. Antigravity, run_command aracını hooks.json ile eşleştirir ve security_gate_hook.sh aracını çalıştırır. Bu araç, push işlemini tanır ve değiştirilen dosyaları tarar: sample_app/data_service.py, tests/test_update_profile.py ve threat_model.md (CodeMender, .md dosyalarını atlar; bu yoldaki cm find, kaynak dosya bulamaz ve kanca devam eder).
Bu işlem birkaç dakika sürer. Kanca eşzamanlı olduğundan, cm find ve cm report çalışırken temsilcinin sırası push komutunda takılı kalmış gibi görünür. Tarama tamamlandığında, CodeMender CLI Basics'in hiçbir zaman düzeltmediği /ping dosyasındaki komut yerleştirmeyi bulur. CRITICAL, OPEN ve gönderilen bir dosyada yer aldığı için kanca reddediyor:
Stage 2: Running Semantic Analysis & Verification (CodeMender)...
Detected 1 blocking vulnerabilit(y/ies). Evaluating attempt budget per finding...
Finding 80bdddd6-... (CRITICAL) in /home/you/codemender-lab/sample_app/data_service.py: Attempt 1/3
{"allow_tool": false, "decision": "deny", "reason": "Security Gate blocked push (1 unresolved finding(s)).
Action Required: Execute the Secure TDD Loop sequentially for each finding:
For each finding below (one at a time):
1. Invoke 'security-test-writer' to author one failing boundary test in tests/ (assert RED).
2. Invoke 'defensive-developer' to apply the minimal defensive fix (assert GREEN).
3. Run the local test to confirm it passes before moving to the next finding.
Once all findings are resolved, run the full regression test suite (python3 -m unittest discover -s tests) and retry 'git push'.
Open Findings:
- [80bdddd6-...] (CRITICAL) in /home/you/codemender-lab/sample_app/data_service.py (Attempt 1/3): OS Command Injection in ping_host endpoint", ...}
Push işlemi çalıştırılmadı. Temsilci, araç çağrısının sonucu olarak reason metnini görür ve ağ geçidi bu bulgu için bir DENIED_TO_AGENT etkinliği kaydeder.
Temsilci, bulguyu test öncelikli olarak temizler.
Reddedilen yanıtta kullanılacak beceriler belirtiliyor ve 6 numaralı kural maddesinde (Çoklu Bulguları Düzeltme Protokolü) bulguların tek tek ele alınması gerektiği söyleniyor. /ping bulgusu için temsili bir çalıştırma şu şekildedir.
tests/test_ping.py içinde başarısız olan bir sınır testi:
import unittest
from sample_app.data_service import app
class PingBoundaryTests(unittest.TestCase):
def setUp(self):
self.client = app.test_client()
def test_rejects_shell_metacharacters(self):
r = self.client.get('/ping', query_string={'ip': '127.0.0.1; id'})
self.assertEqual(r.status_code, 400)
def test_rejects_hostname_that_is_not_an_ip(self):
r = self.client.get('/ping', query_string={'ip': 'example.com'})
self.assertEqual(r.status_code, 400)
CONTEXT.md için gerektiği gibi os.system yerine liste biçiminde subprocess.run kullanan ve dize kontrolleri yerine izin verilenler listesi ayrıştırıcısı içeren minimum düzeyde düzeltme:
import ipaddress
import subprocess
@app.route('/ping')
def ping_host():
ip = request.args.get('ip', '')
try:
target = str(ipaddress.ip_address(ip))
except ValueError:
return jsonify({"error": "ip must be a valid IP address"}), 400
subprocess.run(["ping", "-c", "1", target], shell=False, check=False, timeout=5)
return "Ping initiated"
Temsilci, tüm paketi çalıştırır, bulguyu ve düzeltmeyi açıklayan bir mesajla kaydeder ve tekrar gönderir.
İkinci push işlemine izin verilir.
Kapı, sample_app/data_service.py öğesini yeniden tarar. Komut yerleştirme ortadan kalktı. Engelleme bulgusu kalmadığından kapı kendi günlüğünü kontrol eder, daha önce reddettiği bulgunun artık mevcut olmadığını görür ve aktarıma izin vermeden önce sonucu kaydeder:
Stage 2: Running Semantic Analysis & Verification (CodeMender)...
No blocking vulnerabilities detected. Code verified clean.
Updated CONTEXT.md with auto-evolved convention for 80bdddd6-....
Continuous Evolution Suggestion: Consider updating relevant SKILL.md files (e.g. security-test-writer / defensive-developer) to encode CRITICAL.
{"allow_tool": true, "decision": "allow"}
git push origin main çalışır ve uzak depoda artık üç yeni commit vardır: özellik, düzeltme ve kancanın kendisinin yaptığı bir docs(context) commit'i.
7. Denetim takibini inceleme
Kapının karar verdiği her şey, düz metin olarak diskte yer alır. ~/codemender-lab içindeki bir terminalden:
Kapı günlüğü
jq -c '{ts, event, tool, finding_id, file, attempt}' .security-gate/findings-log.ndjson
{"ts":"2026-11-13T22:41:07Z","event":"DENIED_TO_AGENT","tool":"codemender","finding_id":"80bdddd6-...","file":"/home/you/codemender-lab/sample_app/data_service.py","attempt":1}
{"ts":"2026-11-13T22:58:32Z","event":"FIXED","tool":"codemender","finding_id":"80bdddd6-...","file":"/home/you/codemender-lab/sample_app/data_service.py","attempt":null}
Her satırda ayrıca actor (git e-posta adresiniz) ve commit (o zamanki kısa SHA) bulunur. Bu sayede, daha sonra sorulabilecek "Buna kim, ne zaman izin verdi?" sorusu veritabanı olmadan yanıtlanabilir.
Git geçmişi
git log --oneline -5
a1b2c3d docs(context): record auto-evolved convention for 80bdddd6-...
e4f5a6b fix: validate ip and use subprocess list form in /ping (CWE-78)
c7d8e9f feat: add POST /update_profile with validated input and parameterized update
...
Gelişen bağlam
tail -n 4 CONTEXT.md
## 4. Continuous Evolution: Auto-Evolved Conventions
- **Auto-Evolved Convention (80bdddd6-...)**: In `/home/you/codemender-lab/sample_app/data_service.py`, maintain defensive validation for: CRITICAL.
Kancanın girişi kısa ve öz. Çünkü yalnızca bulgu kimliğini, dosyayı ve önem derecesini biliyor. skill-evolution-updater becerisi, bunu bir insanın yazacağı bir kurala dönüştürmek için kullanılır. Temsilciye şunu sorun: "CONTEXT.md dosyasındaki otomatik olarak geliştirilen son kuralı proje kuralı olarak yeniden yazmak için skill-evolution-updater'ı kullanın ve bu kuralın gerektirdiği SKILL.md değişikliklerini önerin."
CodeMender'ın görünümü
cm report
Komut yerleştirme bulgusu artık OPEN olmayan bir durum göstermelidir. threat_model.md ve iki yeni test dosyası, bir sonraki özelliğin üzerine inşa edileceği depoda yer alır.
8. Temizleme
CodeMender'ın etkileşimli varsayılanlarını geri yükleme:
sed -i.bak 's/^ confirm_commands: false/ confirm_commands: true/; s/^ confirm_writes: false/ confirm_writes: true/' ~/.codemender/config.yaml
rm ~/.codemender/config.yaml.bak
CodeMender'ın yerel durumunu temizleyin (istemde y yazın):
cm clean
Laboratuvar dizinlerini de kaldırmak için:
cd ~ && rm -rf ~/codemender-lab ~/codemender-remote.git
Tebrikler! CodeMender ile uçtan uca temsilci tabanlı güvenlik iş akışı codelab'ini tamamladınız. İlk codelab'de CLI'yı, ikinci codelab'de becerileri ve kancayı, üçüncü codelab'de ise engellenen, düzeltilen, günlüğe kaydedilen ve CodeMender istemi olmadan izin verilen bir push işlemi üzerinde çalışan bir temsilciyi gördünüz.