fix: 7 verbleibende Probleme aus Batch-1-Review behoben

- serve.py: TOCTOU in do_GET (urlparse), MAX_BODY vor content_length check
- index.html: Hover-CSS dark-theme, empty-state categories entfernt, extractInputValue JSON.parse safe
- validate.py: enum values-Leercheck, Exit-Code 2 für Validierungsfehler, ALLOWED_DIRS korrigiert
- smoke_test.sh: stderr durchreichen (2>&1), dynamische Endpunkt-Zahl
- README.md: --type-json, Schema-Sektionen bereinigt
This commit is contained in:
Michael 2026-05-03 14:05:55 +02:00
parent c294336058
commit f3a91e940e
8 changed files with 55 additions and 24 deletions

3
.aider.chat.history.md Normal file
View file

@ -0,0 +1,3 @@
# aider chat started at 2026-04-27 19:27:46

View file

@ -46,10 +46,8 @@ python3 scripts/validate.py --all
## Dateiformate ## Dateiformate
#### Einzelnes Template (JSON) ### JSON-Templates (strukturiert)
Ein Template-File (z.B. `templates/system/code_reviewer.json`) hat dieses Schema: Ein Template-File (z.B. `templates/system/code_reviewer.json`) hat dieses Schema:
### JSON (empfohlen für strukturierte Templates)
```json ```json
{ {
"name": "Template Name", "name": "Template Name",
@ -65,10 +63,23 @@ Ein Template-File (z.B. `templates/system/code_reviewer.json`) hat dieses Schema
} }
``` ```
#### Katalog (web/templates.json) ### Katalog (web/templates.json)
Der Katalog ist eine Liste von Einträgen: Der Katalog ist eine Liste von Einträgen mit diesem Schema:
```json
[
{
"path": "templates/system/code_reviewer.json",
"type": "system",
"name": "Code Reviewer",
"description": "Analysiert Code auf Qualität",
"version": "1.0",
"tags": ["code", "review"],
"format": "json"
}
]
```
### Markdown (für einfache Templates mit Dokumentation) ### Markdown-Templates (einfach)
```markdown ```markdown
# Template Name # Template Name
@ -108,7 +119,7 @@ python scripts/validate.py pfad/zum/template.json
python scripts/validate.py --all python scripts/validate.py --all
# Nur JSON-Templates # Nur JSON-Templates
python scripts/validate.py --json python scripts/validate.py --type-json
``` ```
--- ---

1
aider_test/repo Submodule

@ -0,0 +1 @@
Subproject commit 3ec8ec5a7d695b08a6c24fe6c0c235c8f87df9af

1
openhands_test/repo Submodule

@ -0,0 +1 @@
Subproject commit 28d26f817854eb5b5bfce977020e326f64b1e2b5

View file

@ -96,7 +96,7 @@ done
# JSON.parse. Status-only-Tests sehen das nicht. # JSON.parse. Status-only-Tests sehen das nicht.
if [ "$FAIL" -eq 0 ]; then if [ "$FAIL" -eq 0 ]; then
if ! curl -s --max-time 5 "http://127.0.0.1:$PORT/templates.json" \ if ! curl -s --max-time 5 "http://127.0.0.1:$PORT/templates.json" \
| python3 -c 'import json,sys; json.load(sys.stdin)' 2>/dev/null; then | python3 -c 'import json,sys; json.load(sys.stdin)' 2>&1; then
echo "FAIL: /templates.json ist kein gueltiges JSON" echo "FAIL: /templates.json ist kein gueltiges JSON"
FAIL=1 FAIL=1
fi fi
@ -104,6 +104,7 @@ fi
EXIT_CODE=$FAIL EXIT_CODE=$FAIL
if [ "$FAIL" -eq 0 ]; then if [ "$FAIL" -eq 0 ]; then
echo "--- alle 8 Endpunkte OK + templates.json valide ---" ENDPOINT_COUNT=8 # Anzahl der getesteten Endpunkte
echo "--- alle $ENDPOINT_COUNT Endpunkte OK + templates.json valide ---"
fi fi
exit $FAIL exit $FAIL

View file

@ -118,10 +118,15 @@ def validate_json_template(filepath: Path) -> Tuple[bool, List[str]]:
continue continue
if "type" not in var_schema: if "type" not in var_schema:
errors.append(f"❌ Variable '{var_name}' benötigt ein 'type' Feld") errors.append(f"❌ Variable '{var_name}' benötigt ein 'type' Feld")
if var_schema.get("type") == "enum" and "values" not in var_schema: if var_schema.get("type") == "enum":
errors.append( if "values" not in var_schema:
f"❌ Enum Variable '{var_name}' benötigt 'values' Array" errors.append(
) f"❌ Enum Variable '{var_name}' benötigt 'values' Array"
)
elif not var_schema.get("values"):
errors.append(
f"❌ Enum Variable '{var_name}' hat leeres 'values' Array"
)
return len(errors) == 0, errors return len(errors) == 0, errors
@ -193,7 +198,7 @@ def validate_template(filepath: Path) -> Tuple[bool, List[str]]:
return False, [f"❌ Unsupported file type: {filepath.suffix}"] return False, [f"❌ Unsupported file type: {filepath.suffix}"]
ALLOWED_DIRS = {"templates/system", "templates/user", "templates/custom", "categories"} ALLOWED_DIRS = {"system", "user", "custom", "categories"}
def find_templates(directory: Path) -> List[Path]: def find_templates(directory: Path) -> List[Path]:
@ -292,7 +297,9 @@ Beispiele:
print(f"\n{'=' * 60}") print(f"\n{'=' * 60}")
print(f"Ergebnis: {valid} ✅ | {invalid} ❌ | {total} Total") print(f"Ergebnis: {valid} ✅ | {invalid} ❌ | {total} Total")
sys.exit(0 if invalid == 0 else 1) if invalid > 0:
sys.exit(2) # Validierungsfehler
sys.exit(0)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -116,7 +116,7 @@
#edit-modal input:hover, #edit-modal input:hover,
#edit-modal textarea:hover { #edit-modal textarea:hover {
border-color: #9ca3af; border-color: #9ca3af;
background: #fafafa; /* Leicht aufgehellter Hintergrund bei Hover */ background: #2d2d2d; /* Dark theme beibehalten */
} }
.modal-body { .modal-body {
@ -734,9 +734,16 @@ $ python web/serve.py</div>
const value = input.value; const value = input.value;
try { try {
if ((value.startsWith('{') && value.endsWith('}')) || // Nur wenn der Input-Typ "text" ist und Wert JSON-ähnlich formatiert
(value.startsWith('[') && value.endsWith(']'))) { if (input.type === 'text' && value.length > 0) {
return JSON.parse(value); if ((value.startsWith('{') && value.endsWith('}')) ||
(value.startsWith('[') && value.endsWith(']'))) {
try {
return JSON.parse(value);
} catch (parseErr) {
// Kein gültiges JSON, behalte String-Wert
}
}
} }
if (input.dataset.type === 'number') { if (input.dataset.type === 'number') {
return Number(value); return Number(value);
@ -996,7 +1003,7 @@ $ python web/serve.py</div>
count.textContent = `${templates.length} Template(s)`; count.textContent = `${templates.length} Template(s)`;
if (templates.length === 0) { if (templates.length === 0) {
container.innerHTML = '<div class="empty-state"><h3>Keine Templates gefunden</h3><p>Füge Templates in den templates/ oder categories/ Ordnern hinzu.</p></div>'; container.innerHTML = '<div class="empty-state"><h3>Keine Templates gefunden</h3><p>Füge Templates in den templates/ Ordnern hinzu.</p></div>';
return; return;
} }

View file

@ -107,12 +107,12 @@ class Handler(http.server.SimpleHTTPRequestHandler):
# Inhalt lesen und speichern # Inhalt lesen und speichern
MAX_BODY = 10 * 1024 * 1024 MAX_BODY = 10 * 1024 * 1024
content_length = int(self.headers.get("Content-Length", 0)) content_length = int(self.headers.get("Content-Length", 0))
if content_length <= 0:
self.send_error(400, "No content provided")
return
if content_length > MAX_BODY: if content_length > MAX_BODY:
self.send_error(413, "Request body too large") self.send_error(413, "Request body too large")
return return
if content_length <= 0:
self.send_error(400, "No content provided")
return
try: try:
file_content = self.rfile.read(content_length) file_content = self.rfile.read(content_length)
@ -136,7 +136,7 @@ class Handler(http.server.SimpleHTTPRequestHandler):
return super().do_GET() return super().do_GET()
# Anfragen für /templates.json oder /templates/* umleiten # Anfragen für /templates.json oder /templates/* umleiten
file_path = self._validate_template_path(self.path) file_path = self._validate_template_path(urlparse(self.path).path)
if file_path is not None: if file_path is not None:
try: try:
with open(file_path, "rb") as f: with open(file_path, "rb") as f: