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:
parent
c294336058
commit
f3a91e940e
8 changed files with 55 additions and 24 deletions
3
.aider.chat.history.md
Normal file
3
.aider.chat.history.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
# aider chat started at 2026-04-27 19:27:46
|
||||||
|
|
||||||
25
README.md
25
README.md
|
|
@ -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
1
aider_test/repo
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 3ec8ec5a7d695b08a6c24fe6c0c235c8f87df9af
|
||||||
1
openhands_test/repo
Submodule
1
openhands_test/repo
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 28d26f817854eb5b5bfce977020e326f64b1e2b5
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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":
|
||||||
|
if "values" not in var_schema:
|
||||||
errors.append(
|
errors.append(
|
||||||
f"❌ Enum Variable '{var_name}' benötigt 'values' Array"
|
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__":
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
// Nur wenn der Input-Typ "text" ist und Wert JSON-ähnlich formatiert
|
||||||
|
if (input.type === 'text' && value.length > 0) {
|
||||||
if ((value.startsWith('{') && value.endsWith('}')) ||
|
if ((value.startsWith('{') && value.endsWith('}')) ||
|
||||||
(value.startsWith('[') && value.endsWith(']'))) {
|
(value.startsWith('[') && value.endsWith(']'))) {
|
||||||
|
try {
|
||||||
return JSON.parse(value);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue