1. परिचय
इस कोडलैब में, आपको पिछले दो कोडलैब एक साथ मिलेंगे. Antigravity को सुविधा का अनुरोध दें. इसके बाद, देखें कि यह थ्रेट मॉडल के साथ कैसे प्लान करता है, फ़ेल हो रहे टेस्ट लिखता है, सुविधा लागू करता है, और पुश करता है. पुश करने पर, पिछले कोडलैब का सिक्योरिटी गेट, बदली गई फ़ाइलों के लिए CodeMender को चलाता है. सैंपल ऐप्लिकेशन में अब भी उस फ़ाइल में कमांड इंजेक्शन मौजूद है जिसमें एजेंट बदलाव कर रहा है. इसलिए, पहला पुश ब्लॉक हो जाता है. इसके बाद, आपको दिखेगा कि एजेंट, टेस्ट-फ़र्स्ट की मदद से समस्या को ठीक करता है और फिर से पुश करता है. इस दौरान, CodeMender से कोई मानवीय प्रॉम्प्ट नहीं मिलता.
आपको क्या सीखने को मिलेगा
- CodeMender को हेडलेस तरीके से कैसे चलाएं, ताकि हुक या एजेंट,
[Y/n]प्रॉम्प्ट को ब्लॉक न करे. - जब एजेंट कोई सुविधा बनाता है, तो प्लान → रेड → ग्रीन → रीफ़ैक्टर लूप से क्या जनरेट होता है:
threat_model.md, टेस्ट, कोड, और कमिट. - पुश की गई फ़ाइल में ओपन फ़ाइंडिंग होने पर, सिक्योरिटी गेट कैसे जवाब देता है. साथ ही, एजेंट उन्हें हटाने के लिए
security-test-writerऔरdefensive-developerस्किल का इस्तेमाल कैसे करता है. - गेट से मिलने वाले ऑडिट ट्रेल को पढ़ने का तरीका:
.security-gate/findings-log.ndjson,CONTEXT.mdमें अपने-आप विकसित होने वाले कन्वेंशन, और git का इतिहास.
ज़रूरी शर्तें
- पिछले दो कोडलैब का
~/codemender-labफ़ाइल फ़ोल्डर, जिसमेंmainब्रांच पर एक क्लीन गिट वर्किंग ट्री हो. - Antigravity इंस्टॉल हो गया है.
~/codemender-labको फ़ोल्डर के तौर पर खोला जा सकता है. git,jq, और Flask के साथ इंस्टॉल किया गया Python वर्चुअल एनवायरमेंट.
2. पिछले कोडलैब से सेटअप की पुष्टि करना
पहला चरण: लैब डायरेक्ट्री पर वापस जाना
cd ~/codemender-lab && source .venv/bin/activate
दूसरा चरण: पुष्टि करना कि फ़्रेमवर्क फ़ाइलें सही जगह पर हैं
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
आपको छह स्किल डायरेक्ट्री (defensive-developer, history-context-seeder, local-refactor-scanner, security-test-writer, skill-evolution-updater, threat-model-assessor), tests/__init__.py, और tests/test_data_service.py दिखेंगी. साथ ही, एक खाली git status और main दिखेगा.
यह पुष्टि करने के लिए कि कुछ भी नहीं बदला है, ऑफ़लाइन हुक टेस्ट को एक बार फिर से चलाएं:
bash .agents/tests/run_tests.sh | tail -1
60 passed, 0 failed
तीसरा चरण: CodeMender के एनवायरमेंट की पुष्टि करना
cm init --verify
इसे वीसीएस टाइप के तौर पर git और बिल्ड कमांड के तौर पर python3 -m compileall -q . को रिपोर्ट करना चाहिए. ऐसा CodeMender सीएलआई की बुनियादी बातों में कॉन्फ़िगर किया गया है.
चौथा चरण: पुरानी जानकारी मिटाना
cm clean
जब कहा जाए, तब y टाइप करें. इससे ~/.codemender/state.db खाली हो जाता है.
पांचवां चरण: पुश करने के लिए रिमोट बनाना
हुक, आपके वर्किंग ट्री की तुलना origin/main से करता है. साथ ही, git push को कहीं जाने की ज़रूरत होती है. इसके लिए, उसी मशीन पर मौजूद एक बेयर रिपॉज़िटरी काफ़ी है:
git init --bare ~/codemender-remote.git
git remote add origin ~/codemender-remote.git
git push -u origin main
यह पुश आपके टर्मिनल में होता है. इसलिए, Antigravity हुक इसे नहीं देखता. इसके बाद, एजेंट पुश करता है.
3. CodeMender Headless को चलाया जा रहा है
पहला चरण: पुष्टि करने के लिए मिलने वाले प्रॉम्प्ट की सुविधा बंद करना
डिफ़ॉल्ट रूप से, CodeMender हर कमांड को चलाने और हर फ़ाइल को लिखने से पहले, [Y/n] से अनुमति मांगता है. हुक में, जवाब देने के लिए कोई नहीं होता. ~/.codemender/config.yaml खोलें और दोनों tools फ़्लैग को false पर सेट करें या यह चलाएं:
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
इससे बनने वाला सेक्शन इस तरह दिखना चाहिए:
tools:
confirm_commands: false
confirm_writes: false
दूसरा चरण: पुष्टि करना
cm init --verify
4. सुविधा का अनुरोध
पहला चरण: वर्कस्पेस खोलना
~/codemender-lab को Antigravity में फ़ोल्डर के तौर पर खोलें और नई बातचीत शुरू करें. हमेशा चालू रहने वाला secure_tdd_workflow नियम, अब हर टर्न का हिस्सा है. साथ ही, छह स्किल उपलब्ध हैं.
दूसरा चरण: एजेंट को टास्क दें
यह प्रॉम्प्ट पेस्ट करें. ध्यान दें कि इसमें सुरक्षा के बारे में कुछ नहीं बताया गया है. एजेंट को थ्रेट मॉडलिंग, टेस्ट-फ़र्स्ट डेवलपमेंट, और डिफ़ेंसिव पैटर्न की ओर ले जाना, नियम और स्किल का काम है, न कि प्रॉम्प्ट का:
sample_app/data_service.py में POST /update_profile एंडपॉइंट जोड़ें, ताकि उपयोगकर्ता अपना ईमेल पता अपडेट कर सके. यह फ़ॉर्म फ़ील्ड के तौर पर user_id और email को स्वीकार करता है. बदलाव पूरा होने और टेस्ट सुइट पास होने के बाद, इसे कमिट करें और origin main पर पुश करें.
तीसरा चरण: एजेंट के निर्देशों को मंज़ूरी देना
हुक, टूल-अनुमति सिस्टम के बाहर चलता है. हालांकि, एजेंट के अपने निर्देश नहीं चलते. आपको python3 -m unittest ..., git add/commit/push, और cm के किसी भी कमांड को चलाने के लिए प्रॉम्प्ट दिख सकते हैं. उन्हें अनुमति दें. अगर Antigravity, फ़ैसले को याद रखने का विकल्प देता है, तो उसे स्वीकार करें. इससे इस कोडलैब में बाद में आने वाला फ़िक्स लूप, बार-बार मिलने वाले प्रॉम्प्ट पर नहीं रुकेगा.
5. एजेंट क्या करता है: प्लान, रेड, ग्रीन, रीफ़ैक्टर
यहां एक उदाहरण दिया गया है. आपके एजेंट के शब्द, फ़ाइल के नाम, और सटीक कोड अलग-अलग होंगे. हालांकि, इससे कोई फ़र्क़ नहीं पड़ता. इनका क्रम नहीं बदलना चाहिए: टेस्ट से पहले थ्रेट मॉडल, कोड से पहले फ़ेल होने वाले टेस्ट, और कमिट से पहले ग्रीन सुइट.
पहला चरण: प्लान और थ्रेट मॉडल
एजेंट, फ़ाइल फ़ोल्डर के रूट में CONTEXT.md को पढ़ता है, threat-model-assessor को लोड करता है, और threat_model.md को लिखता है:
# 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.
दूसरा चरण: टेस्ट फ़ेल होना (लाल)
एजेंट, security-test-writer को लोड करता है और tests/test_update_profile.py बनाता है. आम तौर पर, कोई वर्शन वर्किंग डायरेक्ट्री में थ्रोअवे users.db का इस्तेमाल करता है. इसलिए, पिछले कोडलैब ने .gitignore में users.db को जोड़ा था:
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)
एजेंट python3 -m unittest discover -s tests को चलाता है और पुष्टि करता है कि नए टेस्ट 404 != 200 और 404 != 400 के साथ फ़ेल हो गए हैं. ऐसा इसलिए होता है, क्योंकि रूट अभी तक मौजूद नहीं है. यह असफल होने की सही वजह है. इंपोर्ट करने में हुई गड़बड़ी या टाइपो की वजह से होने वाली गड़बड़ी को RED नहीं माना जाता. यह एक ब्रोकन टेस्ट है.
फ़ेज़ C: कम से कम लागू करना (हरा)
एजेंट, defensive-developer को लोड करता है और एंडपॉइंट जोड़ता है. यह पैरामीटर वाली क्वेरी का इस्तेमाल करता है, क्योंकि CONTEXT.md इसे SQL के लिए ज़रूरी पैटर्न के तौर पर नाम देता है:
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
अब सुइट पास हो जाता है: चार नए टेस्ट के साथ-साथ, पिछले कोडलैब का कैरेक्टराइज़ेशन टेस्ट.
चौथा चरण: रीफ़ैक्टर, स्कैन, और कमिट करना
एजेंट, local-refactor-scanner को लोड करता है, git diff की समीक्षा करता है, पूरे सुइट को फिर से चलाता है, और कमिट करता है:
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 पर सिक्योरिटी गेट
पहली बार पुश करने पर, उसे अस्वीकार कर दिया जाता है
एजेंट git push origin main को रन करता है. Antigravity, run_command टूल को hooks.json से मैच करता है और security_gate_hook.sh को चलाता है. इससे पुश की पहचान होती है और बदली गई फ़ाइलों को स्कैन किया जाता है: sample_app/data_service.py, tests/test_update_profile.py, और threat_model.md (CodeMender, .md फ़ाइलों को छोड़ देता है; उस पाथ पर cm find को कोई सोर्स फ़ाइल नहीं मिलती और हुक आगे बढ़ जाता है).
इसमें कुछ मिनट लगते हैं. हुक सिंक्रोनस होता है. इसलिए, cm find और cm report के चलने के दौरान, एजेंट की बारी पुश कमांड पर हैंग होती हुई दिखती है. स्कैन पूरा होने पर, गेट को /ping में कमांड इंजेक्शन मिलता है. इसे CodeMender CLI Basics ने कभी ठीक नहीं किया. यह CRITICAL है, यह OPEN है, और यह पुश की जा रही फ़ाइल में है. इसलिए, हुक इसे अस्वीकार कर देता है:
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", ...}
पुश नहीं चला. एजेंट को टूल कॉल के नतीजे के तौर पर reason टेक्स्ट दिखता है. साथ ही, गेट ने इस नतीजे के लिए DENIED_TO_AGENT इवेंट लॉग किया है.
एजेंट, सबसे पहले टेस्ट-फ़र्स्ट की जांच करता है
अस्वीकार करने की वजह में, इस्तेमाल की जाने वाली स्किल के नाम बताए गए हैं. साथ ही, नियम आइटम 6 (एक से ज़्यादा गड़बड़ियों को ठीक करने का प्रोटोकॉल) में बताया गया है कि गड़बड़ियों को एक-एक करके ठीक करें. /ping नतीजे के लिए, प्रतिनिधि रन कुछ इस तरह दिखता है.
tests/test_ping.py में बाउंड्री टेस्ट पूरा नहीं हो पाया:
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)
सबसे आसान तरीका यह है कि os.system को सूची के फ़ॉर्मैट में subprocess.run से बदलें, जैसा कि CONTEXT.md के लिए ज़रूरी है. साथ ही, स्ट्रिंग की जांच करने के बजाय, अनुमति वाली सूची के पार्सर का इस्तेमाल करें:
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"
एजेंट, पूरे सुइट को चलाता है. साथ ही, समस्या और उसे ठीक करने के तरीके के बारे में बताते हुए, एक मैसेज के साथ कमिट करता है और फिर से पुश करता है.
दूसरी बार पुश करने की अनुमति है
गेट, sample_app/data_service.py को फिर से स्कैन करता है. कमांड इंजेक्शन की समस्या ठीक हो गई है. ब्लॉक करने वाली कोई समस्या नहीं बची है. इसलिए, गेट अपने लॉग की जांच करता है और देखता है कि पहले जिस समस्या को अस्वीकार किया गया था वह अब मौजूद नहीं है. इसके बाद, पुश की अनुमति देने से पहले नतीजे को रिकॉर्ड करता है:
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 चलता है और रिमोट में अब तीन नए कमिट हैं: सुविधा, गड़बड़ी ठीक करना, और एक docs(context) कमिट, जो हुक ने खुद बनाया है.
7. ऑडिट ट्रेल की समीक्षा करना
गेट ने जो भी फ़ैसला लिया है वह डिस्क पर सादे टेक्स्ट में मौजूद है. ~/codemender-lab में मौजूद किसी टर्मिनल से:
गेट लॉग
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}
हर लाइन में actor (आपका git ईमेल) और commit (उस समय का छोटा SHA) भी होता है. इससे बाद में, "इसकी अनुमति किसने और कब दी" जैसे सवाल का जवाब, डेटाबेस के बिना दिया जा सकता है.
Git का इतिहास
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
...
बदलता हुआ कॉन्टेक्स्ट
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.
हुक की एंट्री कम शब्दों में होती है, क्योंकि इसमें सिर्फ़ फ़ाइंडिंग आईडी, फ़ाइल, और गंभीरता की जानकारी होती है. skill-evolution-updater स्किल का इस्तेमाल करके, इसे ऐसे नियम में बदला जाता है जिसे कोई इंसान लिखता. एजेंट से कहें: "skill-evolution-updater का इस्तेमाल करके, CONTEXT.md में अपने-आप विकसित हुए आखिरी कन्वेंशन को प्रोजेक्ट के नियम के तौर पर फिर से लिखें. साथ ही, SKILL.md में होने वाले किसी भी बदलाव का सुझाव दें."
CodeMender का व्यू
cm report
कमांड इंजेक्शन की समस्या का स्टेटस अब OPEN नहीं दिखना चाहिए. साथ ही, threat_model.md और दो नई टेस्ट फ़ाइलें, रिपॉज़िटरी में मौजूद होनी चाहिए, ताकि अगली सुविधा बनाई जा सके.
8. सफ़ाई करना
CodeMender की इंटरैक्टिव डिफ़ॉल्ट सेटिंग वापस लाएं:
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 का लोकल स्टेट मिटाएं (प्रॉम्प्ट पर y टाइप करें):
cm clean
लैब डायरेक्ट्री भी हटाने के लिए:
cd ~ && rm -rf ~/codemender-lab ~/codemender-remote.git
बधाई हो! आपने CodeMender कोडलैब की मदद से, एंड-टू-एंड एजेंटिक सिक्योरिटी वर्कफ़्लो को पूरा कर लिया है. आपने पहले कोडलैब में सीएलआई, दूसरे कोडलैब में स्किल और हुक के बारे में जाना. साथ ही, आपने एक ऐसे एजेंट के बारे में जाना जो पुश के ज़रिए काम करता है. इस पुश को ब्लॉक किया गया, ठीक किया गया, लॉग किया गया, और CodeMender प्रॉम्प्ट के बिना अनुमति दी गई.