codedictate ist eine Flask-basierte Diktierloesung, die OpenAI Whisper zur Spracherkennung nutzt. Das Projekt zeigt solide Grundarchitektur, hat aber erhebliche Sicherheitsluecken (hartcodierte Secrets, fehlende Authentifizierung), unzureichende Testabdeckung und mehrere tote Codepfade.
Die Abhaengigkeiten sind teilweise veraltet mit bekannten Schwachstellen.
Sieh diesen Report als Leitfaden, nicht als Zeugnis. Jeder Hinweis enthält einen konkreten nächsten Schritt — oft reicht ein einziger KI-Prompt.
Die drei wichtigsten nächsten Schritte
Du bist auf dem richtigen Weg. Jeder Fix macht deinen Code besser für dich und für die KI. Bleib dran!
Dein Code hat wahrscheinlich ähnliche Probleme. 16 KI-Agenten finden sie für dich — in 15-45 Minuten.
Report für dein Projekt holen — EUR 19KI-Assistenten packen alles in eine große Funktion. Das funktioniert, bis du etwas änderst — dann bricht alles zusammen. Wenn eine Funktion >50 Zeilen hat oder >3 Verschachtelungsebenen, bitte die KI, sie aufzuteilen.
Fuenf verschachtelte try/except-Bloecke machen den Kontrollfluss nahezu unmoeglich nachzuvollziehen.
Fehler werden auf der falschen Ebene gefangen, was zu stillen Fehlern und schwer auffindbaren Bugs fuehrt.
"In server/transcribe.py starting at line 128, flatten the 5 nested try/except blocks by extracting each into a separate function that raises specific exceptions."
try:
try:
try:
result = whisper.transcribe(chunk)
except APIError:
try:
result = whisper.transcribe(chunk, model="base")
except:
...Verschachtelte Fehlerbehandlung durch Funktionsextraktion abflachen. Jede Funktion behandelt eine Aufgabe und deren spezifische Fehler.
app.py importiert models.py, und models.py importiert app.py fuer die db-Instanz. Dies wird durch verzoegerte Imports umgangen, was den Code fragil macht.
Jede Umstrukturierung kann zu ImportError fuehren. Der Code ist schwer zu testen, da die Import-Reihenfolge kritisch ist.
"Create server/db.py exporting the SQLAlchemy db instance. Update server/app.py and server/models.py to import from server/db.py instead of each other."
# server/app.py
from server.models import User, Transcription
# server/models.py
from server.app import db # circular!Zirkulaere Imports deuten auf schlechte Modulgrenzen hin. Gemeinsame Abhaengigkeiten in ein separates Modul extrahieren.
Konfigurationswerte sind ueber config.py, app.py, models.py, transcribe.py, whisper_api.py und setup.py verteilt, mit teilweise widersprüchlichen Defaults.
Inkonsistente Konfiguration fuehrt zu schwer reproduzierbaren Bugs, besonders zwischen Entwicklung und Produktion.
"Consolidate all configuration into server/config.py with Development/Production/Testing classes. Update all other files to import from config."
# config.py: WHISPER_MODEL = "medium"
# transcribe.py: MODEL = os.getenv("MODEL", "small") # conflicts!
# whisper_api.py: DEFAULT_MODEL = "base" # another conflict!Konfiguration sollte an einem Ort leben. Eine einzige Wahrheitsquelle eliminiert Konfigurationsdrift.
Der Chunking-Algorithmus implementiert Silence-Detection, Overlap-Handling und dynamische Chunk-Groesse in einer 120-Zeilen-Funktion mit 8 lokalen Variablen.
Schwer zu debuggen wenn Audioqualitaet variiert. Kleinste Aenderungen koennen das Transcription-Ergebnis dramatisch verschlechtern.
"Refactor chunk_audio() in server/audio.py to use pydub.silence.split_on_silence() instead of the custom implementation. Extract constants to config."
def chunk_audio(audio_data, sr=16000):
threshold = 0.015 # magic number
min_silence = int(sr * 0.3) # magic number
# ... 120 lines of sliding window logicFuer gaengige Audio-Verarbeitungsaufgaben gut getestete Bibliotheken (pydub, librosa) statt eigener Implementierungen bevorzugen.
client/ui.py enthaelt sowohl GUI-Rendering als auch Geschaeftslogik (Dateiauswahl, API-Aufrufe, Fehlerbehandlung) in einer 450-Zeilen-Datei.
Aenderungen am Layout koennen Geschaeftslogik brechen und umgekehrt. Unit-Tests sind praktisch unmoeglich.
"Split client/ui.py into three modules: client/ui.py (rendering only), client/api.py (API calls), client/controller.py (business logic coordination)."
class MainWindow:
def on_record_click(self):
# 80 lines mixing UI updates, API calls, and error handling
self.status_label.config(text="Recording...")
audio = self.recorder.record()
response = requests.post(API_URL + "/upload", ...)Darstellung von Logik trennen. MVC oder aehnliche Muster machen Code testbar und wartbar.
Eine einzelne Funktion behandelt Audio-Dekodierung, Chunking, Whisper-API-Aufrufe, Postprocessing, Interpunktion und Datenbankschreiben — alles in 380 Zeilen.
Praktisch nicht testbar, extrem fehleranfaellig bei Aenderungen. Jeder Bug-Fix kann unbeabsichtigte Seiteneffekte haben.
"Refactor transcribe_and_process() in server/transcribe.py into 5 smaller functions: decode_audio(), chunk_audio(), call_whisper(), postprocess_text(), save_transcription(). Wire them together in a pipeline function."
def transcribe_and_process(audio_file, user_id, language="de"):
# ... 380 lines of nested logic
# audio decoding, chunking, API calls, text cleanup, DB writesFunktionen ueber 50 Zeilen sind ein Warnsignal. Ueber 100 ist gefaehrlich. Ueber 300 ist ein Wartungsalptraum. An Verantwortlichkeitsgrenzen aufteilen.
Der /transcribe-Endpunkt hat 28 verschiedene Entscheidungspfade durch verschachtelte Bedingungen und Fehlerbehandlung.
Bei jeder Aenderung muessen theoretisch 28 Pfade getestet werden. Das erhoehte Regressionsrisiko verlangsamt die Entwicklung.
"Refactor the /transcribe endpoint in server/app.py to use guard clauses for early returns and extract validation, processing, and response formatting into separate functions."
@app.route("/transcribe", methods=["POST"])
def transcribe():
if request.content_type:
if "multipart" in request.content_type:
if "audio" in request.files:
# ... 15 more levels of nestingZyklomatische Komplexitaet unter 10 anstreben. Guard-Clauses (fruehe Returns) verwenden, um verschachtelte Bedingungen abzuflachen.
Beim Vibe Coding ist Testen die einzige Garantie. Jeder neue Prompt kann alten Code brechen. Goldene Regel: Schreibe einen Test, der den aktuellen Stand beweist, BEVOR du die KI um Änderungen bittest.
Kein einziger API-Endpunkt wird mit HTTP-Anfragen getestet. Weder Upload noch Transkription noch Admin-Routen haben Integrationstests.
Routing-Fehler, falsche HTTP-Statuscodes und Serialisierungsprobleme werden erst in Produktion entdeckt.
"Create tests/test_api.py using Flask test client. Test all routes: GET /health, POST /upload, POST /transcribe, GET /transcriptions, /admin/* with both valid and invalid inputs."
# No integration tests exist. Example of what should be:
# def test_upload_invalid_file(client):
# response = client.post("/upload", data={"audio": (BytesIO(b"not audio"), "test.exe")})
# assert response.status_code == 400Jeder API-Endpunkt braucht mindestens einen Happy-Path- und einen Fehlerfall-Integrationstest mit dem Framework-Test-Client.
Testdaten werden inline erstellt ohne wiederverwendbare Fixtures. Jeder neue Test muss seinen eigenen Setup-Code schreiben.
Duplizierter Setup-Code fuehrt zu inkonsistenten Testdaten und macht Tests schwer wartbar.
"Create tests/conftest.py with fixtures: app (Flask test app), client (test client), db_session (test database), sample_audio (test audio file). Create tests/factories.py for User and Transcription factories."
# Current: no fixtures, each test duplicates setup
def test_something():
app = create_app() # duplicated
db.create_all() # duplicated
user = User(email="test@test.com") # duplicatedGute Test-Infrastruktur (Fixtures, Factories, Helper) zahlt sich innerhalb von Wochen aus, indem sie Tests einfach zu schreiben und zu warten macht.
Der einzige Mock fuer die Whisper-API gibt ein vereinfachtes Objekt zurueck, das nicht dem tatsaechlichen API-Antwortformat entspricht.
Tests bestehen, obwohl der Code mit der echten API fehlschlagen wuerde. Falsche Sicherheit.
"In tests/test_transcribe.py, update the Whisper API mock to return the actual response format including segments, language, and duration fields."
# Mock returns simplified format:
mock_whisper.return_value = {"text": "hello world"}
# Real API returns: {"text": "...", "segments": [...], "language": "de", "duration": 12.5}Mocks muessen der realen Schnittstelle entsprechen. Echte Antworten aufzeichnen und als Fixtures verwenden, um Drift zu verhindern.
Das gesamte Projekt hat nur 2 Tests in einer einzigen Testdatei. Kernfunktionalitaet wie Upload, Transkription und Authentifizierung ist nicht getestet.
Jede Aenderung kann unbemerkt bestehende Funktionalitaet zerstoeren. Refactoring wird zum Gluecksspiel.
"Set up pytest with pytest-flask. Create test files: tests/test_api.py (endpoint tests), tests/test_transcribe.py (transcription logic), tests/test_models.py (database operations). Target 60% coverage minimum."
# tests/test_transcribe.py — ENTIRE test suite:
def test_whisper_returns_text():
assert transcribe("hello.wav") != ""
def test_empty_audio():
assert transcribe("empty.wav") == ""Testabdeckung unter 40% bedeutet Blindflug. Kritische Pfade priorisieren: Auth, Zahlung, Datenpersistenz.
Die vorhandenen Tests verbinden sich mit der gleichen Datenbank wie die Produktion, da keine Test-Konfiguration existiert.
Tests koennen Produktionsdaten veraendern oder loeschen. Ein versehentlicher Testlauf kann echte Benutzerdaten zerstoeren.
"Create tests/conftest.py with a test database fixture using SQLite in-memory. Update tests to use the fixture instead of importing from server.config directly."
# tests/test_transcribe.py
from server.config import DATABASE_URL # same as production!
from server.models import db
def test_save_transcription():
db.session.add(...) # writes to production DB!Tests duerfen niemals Produktionsdatenbanken beruehren. Separate Testdatenbanken, Fixtures und Cleanup verwenden.
Es gibt weder GitHub Actions, GitLab CI noch andere CI/CD-Konfiguration. Tests muessen manuell ausgefuehrt werden.
Ohne automatisierte Testausfuehrung werden Tests vergessen und die Codequalitaet erodiert schleichend.
"Create .github/workflows/test.yml that runs pytest on every push and PR. Include ruff linting. Add a badge to README.md."
# No CI/CD configuration files found:
# No .github/workflows/*.yml
# No .gitlab-ci.yml
# No JenkinsfileCI/CD ist nicht verhandelbar fuer jedes Team-Projekt. Linting und Tests ab Tag eins automatisieren.
Toter Code sammelt sich in KI-Sessions an — alte Ansätze, die zurückbleiben. Er verwirrt sowohl dich als auch zukünftige KI-Prompts. Halte den Code sauber: was nicht gebraucht wird, wird gelöscht.
server/whisper_api.py wird von keinem anderen Modul importiert. Die Whisper-Integration erfolgt direkt in transcribe.py.
Totes Modul verwirrt neue Entwickler und wird bei Refactoring-Aufgaben versehentlich mitgeaendert.
"Delete server/whisper_api.py — it is not imported anywhere. Run grep -r "whisper_api" to confirm no references exist."
# server/whisper_api.py — 180 lines, imported by nobody
class WhisperClient:
def __init__(self, api_key, model="medium"):
...
def transcribe(self, audio_path, language="de"):
...Toter Code ist nicht kostenlos. Er kostet Aufmerksamkeit, erzeugt Verwirrung und kann Bugs verursachen, wenn er versehentlich geaendert wird.
14 importierte Module oder Funktionen werden nie verwendet: json, sys, re in app.py, hashlib und hmac in auth.py, u.a.
Unbenutzte Imports verlangsamen den Start, erhoehen den Memory-Footprint und verschleiern echte Abhaengigkeiten.
"Run ruff check --select F401 --fix server/ to auto-remove all unused imports. Then run ruff check --select I --fix server/ to sort remaining imports."
import json # unused
import sys # unused
import re # unused
from flask import Flask, request, jsonify, redirect # redirect unusedEinen Auto-Formatter (ruff, autoflake) verwenden, um unbenutzte Imports zu finden. Als Pre-commit Hook konfigurieren, um Anhaeufung zu verhindern.
65 Zeilen auskommentierter WebSocket-Code fuer Echtzeit-Streaming blockieren die Lesbarkeit ohne Nutzen.
Auskommentierter Code wird nie wieder aktiviert, verwirrt aber Entwickler und erschwert Code-Reviews.
"Delete the commented-out WebSocket streaming code at server/app.py lines 245-310. Create a GitHub issue "Implement real-time WebSocket streaming" if the feature is still planned."
# TODO: WebSocket streaming (v2)
# @socketio.on("audio_stream")
# def handle_stream(data):
# chunk = data["chunk"]
# # ... 60 more commented linesAuskommentierten Code loeschen. Git bewahrt die History. Tote Kommentare sind Rauschen, keine Dokumentation.
Nach einem return-Statement in Zeile 288 folgen 12 Zeilen Code, die niemals ausgefuehrt werden.
Entwickler koennten annehmen, dass dieser Code ausgefuehrt wird, und Bugs auf Basis falscher Annahmen einbauen.
"Delete the unreachable code at server/transcribe.py lines 290-302 (after the return at line 288). Verify the return at 288 is intentional."
return result # line 288
# Dead code below — never executes:
logger.info("Post-processing complete")
stats.record_transcription(len(result))
cache.set(cache_key, result)Unerreichbarer Code nach Return-Statements ist ein haeufiger Fehler. Eine Linter-Regel verwenden, um ihn automatisch zu erkennen.
Eine Funktion migrate_v1_to_v2() in utils.py wurde fuer eine einmalige Datenmigration geschrieben und wird nie wieder aufgerufen.
Minimales Risiko, aber erhoeht die kognitive Last beim Lesen von utils.py.
"Delete the migrate_v1_to_v2() function at server/utils.py lines 78-120. It was a one-time migration and is no longer called."
def migrate_v1_to_v2():
"""One-time migration from v1 schema to v2. Run once, then delete."""
# ... 40 lines of migration logic
# Last run: 2024-08-15Einmalige Skripte sollten in einem separaten Verzeichnis (z.B. migrations/) leben oder nach Ausfuehrung geloescht werden.
KI wiederholt sich zwischen Prompts. Inkonsistente Benennung, doppelte Funktionen. Jedes Problem ist klein, aber zusammen wird Code unwartbar. Prüfe regelmäßig.
except: ohne spezifische Exception faengt alles ein, einschliesslich SystemExit, KeyboardInterrupt und MemoryError.
Prozess kann nicht sauber beendet werden. Schwerwiegende Systemfehler werden verschluckt und bleiben unbemerkt.
"In server/transcribe.py, replace all bare except: clauses with except Exception as e: and add logging.exception("...") calls."
try:
result = process_audio(chunk)
except: # catches EVERYTHING
result = "" # silently returns empty stringNiemals blankes except: verwenden. Immer spezifische Exceptions fangen oder mindestens except Exception.
Mehrere veraenderbare globale Variablen (active_jobs, cache_dict, stats_counter) werden ohne Synchronisation zwischen Requests geteilt.
Race Conditions unter Last koennen zu Datenverlust, korrupten Zaehlerstaenden oder Cache-Inkonsistenzen fuehren.
"Replace global mutable state in server/transcribe.py with thread-safe alternatives: use threading.Lock for active_jobs, use Flask-Caching for cache_dict, use Redis or atomic operations for stats_counter."
active_jobs = {} # mutable global, shared across threads
cache_dict = {} # mutable global, no TTL, no size limit
stats_counter = {"total": 0, "errors": 0} # race condition!Globaler veraenderbarer Zustand ist der Feind von nebenlaeufigem Code. Thread-sichere Strukturen oder externe State-Stores verwenden.
Zahlenwerte wie 16000, 0.015, 0.3, 512 werden direkt im Code verwendet ohne erklaerende Konstanten.
Schwer zu verstehen was die Zahlen bedeuten. Aenderungen erfordern Suchen-und-Ersetzen ueber mehrere Dateien.
"Extract magic numbers in server/audio.py to named constants: SAMPLE_RATE = 16000, SILENCE_THRESHOLD = 0.015, MIN_SILENCE_SECONDS = 0.3, FFT_SIZE = 512."
audio = audio.set_frame_rate(16000) # what is 16000?
if amplitude < 0.015: # what threshold?
if silence_frames > int(16000 * 0.3): # ??Jede magische Zahl benennen. SAMPLE_RATE = 16000 ist selbstdokumentierend; 16000 allein nicht.
Verschiedene Endpunkte geben Fehler in unterschiedlichen Formaten zurueck: mal {"error": "..."}, mal {"message": "..."}, mal Klartext.
Clients muessen verschiedene Fehlerformate behandeln, was zu unzuverlaessiger Fehleranzeige fuehrt.
"Create an error handler in server/app.py using @app.errorhandler that returns {"error": {"code": status_code, "message": str(error)}} for all error responses."
# Endpoint A:
return jsonify({"error": "File too large"}), 400
# Endpoint B:
return jsonify({"message": "Invalid format"}), 422
# Endpoint C:
return "Server error", 500Fehlerantworten in der API standardisieren. Clients sollten Fehler ueberall gleich parsen koennen.
Textfelder wie title und notes akzeptieren beliebig lange Strings ohne Laengenbeschraenkung auf Anwendungsebene.
Extrem lange Eingaben koennen die Datenbank belasten und UI-Elemente brechen.
"Add length constraints to server/models.py: title = Column(String(200), nullable=False), notes = Column(String(5000)). Add validation in the route handlers."
class Transcription(db.Model):
title = db.Column(db.String) # no length limit
notes = db.Column(db.Text) # no length limitImmer maximale Laengen fuer benutzerbereitgestellte Textfelder definieren. Unbegrenzte Eingaben sind ein Sicherheits- und Stabilitaetsrisiko.
API-Aufrufe an den Whisper-Service haben keinen Timeout, kein Retry und keine spezifische Fehlerbehandlung. Ein 500er oder Timeout fuehrt zum Absturz des gesamten Request-Handlers.
Transiente API-Fehler fuehren zu komplettem Fehlschlag der Transkription. Benutzer verlieren ihre Aufnahme ohne Fehlermeldung.
"Wrap Whisper API calls in server/transcribe.py with tenacity retry decorator: @retry(stop=stop_after_attempt(3), wait=wait_exponential(min=1, max=10)). Add timeout=30 to requests."
response = openai.audio.transcriptions.create(
model=WHISPER_MODEL,
file=audio_chunk,
language=language
) # no timeout, no retry, no error handlingAlle externen API-Aufrufe brauchen Timeout, Retry und Fehlerbehandlung. Davon ausgehen, dass das Netzwerk ausfallen wird.
Das gesamte Projekt verwendet print() fuer Ausgaben statt des Python logging-Moduls. 47 print()-Aufrufe verteilt auf 6 Dateien.
Keine Log-Levels, keine strukturierte Ausgabe, keine Moeglichkeit Logs in Produktion zu filtern oder an Log-Aggregatoren zu senden.
"Replace all print() calls with proper logging: import logging; logger = logging.getLogger(__name__); replace print("Error:...") with logger.error("..."), print("Processing...") with logger.info("...")."
print(f"Processing file: {filename}") # should be logger.info
print(f"ERROR: {str(e)}") # should be logger.error
print(f"Transcription complete in {elapsed}s") # should be logger.infoDas logging-Modul von Tag eins verwenden. Strukturierte Logs mit Levels sind essentiell fuer Produktion-Debugging.
Dokumentation ist die Brücke zwischen dir und deinem zukünftigen Ich (und der KI). Fehlende Docs = häufigster Grund, warum KI-Projekte aufgegeben werden. Die KI kann sie schreiben — du musst nur fragen.
Die README.md enthaelt nur "# codedictate" und eine einzeilige Beschreibung. Installation, Nutzung, API-Dokumentation und Architektur fehlen.
Neue Entwickler koennen das Projekt nicht aufsetzen oder verstehen, ohne den Code zu lesen.
"Expand README.md with sections: ## Features, ## Requirements (Python 3.10+, FFmpeg, OpenAI API key), ## Installation, ## Quick Start, ## API Endpoints, ## Configuration, ## Testing."
# codedictate
Voice-to-text dictation tool.Eine gute README ist die kosteneffektivste Dokumentation. Sie spart Stunden an Einarbeitungszeit pro Entwickler.
Keiner der 8 API-Endpunkte hat Dokumentation. Weder OpenAPI/Swagger noch inline Docstrings beschreiben Request/Response-Formate.
Frontend-Entwickler muessen den Backend-Code lesen, um die API zu verstehen. Integration wird zum Ratespiel.
"Add docstrings to all route handlers in server/app.py with format: Args (JSON body fields), Returns (response format), Raises (error cases). Consider adding flask-openapi3."
@app.route("/transcribe", methods=["POST"])
def transcribe():
# No docstring, no type hints, no documentation
file = request.files["audio"]
...API mindestens mit Docstrings dokumentieren. Fuer oeffentliche APIs OpenAPI/Swagger fuer interaktive Dokumentation verwenden.
82% der Funktionen im Projekt haben keinen Docstring. Nur 5 der 28 Funktionen beschreiben was sie tun, was sie erwarten und was sie zurueckgeben.
Entwickler muessen die Implementierung lesen, um die Schnittstelle zu verstehen. Erhoeht die Einarbeitungszeit erheblich.
"Add Google-style docstrings to all public functions in server/transcribe.py, server/audio.py, and server/models.py. Include Args, Returns, and Raises sections."
def chunk_audio(audio_data, sr=16000):
# No docstring — what does it return? What format is audio_data?
...
def postprocess(text, language):
# No docstring — what postprocessing? What languages supported?
...Docstrings sind der Vertrag zwischen Funktionen. Sie sollten beantworten: was tut sie, was braucht sie, was gibt sie zurueck.
Es gibt weder eine .env.example noch Dokumentation welche Umgebungsvariablen benoetigt werden.
Neue Entwickler oder Deployments scheitern, weil benoetigte Variablen nicht gesetzt sind.
"Create .env.example with all environment variables used in server/config.py: OPENAI_API_KEY, DATABASE_URL, FLASK_SECRET_KEY, FLASK_DEBUG, WHISPER_MODEL. Add descriptions as comments."
# config.py uses these but nobody documents them:
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") # required but undocumented
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///local.db")
SECRET_KEY = os.getenv("FLASK_SECRET_KEY", "dev-key-change-me")Eine .env.example-Datei ist eine guenstige Versicherung. Sie dokumentiert Anforderungen und verhindert Deployment-Fehler.
Keine Dokumentation darueber, warum bestimmte Technologie-Entscheidungen getroffen wurden (Whisper-Modellwahl, SQLite vs. PostgreSQL, Client-Architektur).
Zukuenftige Entwickler wiederholen Evaluierungen oder aendern Entscheidungen ohne den Kontext zu kennen.
"Create docs/adr/001-whisper-model-selection.md and docs/adr/002-sqlite-for-development.md using the lightweight ADR template (Context, Decision, Consequences)."
# No architecture documentation found.
# Questions that remain unanswered:
# - Why Whisper medium model instead of large?
# - Why SQLite instead of PostgreSQL?
# - Why desktop client instead of web-only?Architecture Decision Records (ADRs) verhindern das Wiederholen vergangener Diskussionen. Selbst Ein-Absatz-ADRs sparen zukuenftig Zeit.
KI kennt Muster, folgt ihnen aber nicht immer. Sie mischt async/await mit .then(), ignoriert Konventionen. Sage der KI, welche Muster dein Projekt verwendet — sie wird sich daran halten.
Die .gitignore-Datei enthaelt keine Eintraege fuer .env, *.pem, *.key oder Upload-Verzeichnisse. Sensible Dateien koennten versehentlich committed werden.
Ein versehentliches git add . committet API-Schluessel, Zertifikate und Benutzerdaten ins Repository.
"Update .gitignore to include: .env, .env.*, *.pem, *.key, uploads/, *.sqlite, *.db, __pycache__/, .pytest_cache/, htmlcov/."
# .gitignore (incomplete):
__pycache__/
*.pyc
# Missing: .env, *.pem, *.key, uploads/, *.sqliteJedes Projekt mit einer umfassenden .gitignore beginnen. gitignore.io oder GitHub-Templates als Basis verwenden.
Keine Dokumentation oder Konfiguration fuer Virtual Environments. Entwickler installieren Abhaengigkeiten moeglicherweise global.
Globale Installationen fuehren zu Versionskonflikten zwischen Projekten und machen Builds nicht reproduzierbar.
"Create a Makefile with: setup target (creates venv, installs deps), run target (starts server), test target (runs tests). Add .venv/ to .gitignore."
# No Makefile, no setup script, no venv documentation
# Developers are expected to... guess?
# pip install -r requirements.txt # global? venv? who knows!Immer Virtual Environments verwenden. Den Setup-Prozess dokumentieren. Auf einen Befehl reduzieren.
Kein /health oder /readiness Endpunkt fuer Monitoring, Load Balancer oder Container-Orchestrierung.
Load Balancer und Monitoring-Tools koennen den Zustand der Anwendung nicht pruefen.
"Add a /health endpoint to server/app.py that checks: database connectivity (SELECT 1), returns {"status": "healthy", "db": "ok"} or 503 with details."
# No health check endpoint exists.
# Docker and monitoring have no way to check if the app is running correctly.Health-Check-Endpunkte sind essentiell fuer Produktion. Sie ermoeglichen automatisierte Wiederherstellung und Monitoring.
Der Flask SECRET_KEY hat einen hartcodierten Fallback "dev-key-change-me", der in Produktion verwendet wird, wenn die Umgebungsvariable nicht gesetzt ist.
Mit bekanntem Secret Key koennen Angreifer Session-Cookies signieren und Admin-Zugriff erlangen.
"In server/config.py, change SECRET_KEY to raise an error if not set: SECRET_KEY = os.environ["FLASK_SECRET_KEY"] # no fallback, must be set."
SECRET_KEY = os.getenv("FLASK_SECRET_KEY", "dev-key-change-me") # predictable!Niemals Standardwerte fuer sicherheitskritische Konfiguration bereitstellen. Laut fehlschlagen statt unsicher laufen.
Kein Code-Formatter (Black, Ruff) konfiguriert. Der Code hat inkonsistente Einrueckung, Zeilenlaengen und String-Quoting.
Inkonsistenter Stil erzeugt unnoetige Diff-Noise in Code-Reviews und verlangsamt das Lesen.
"Add ruff configuration to pyproject.toml: [tool.ruff] line-length = 100. Create .pre-commit-config.yaml with ruff check and ruff format hooks. Run ruff format . on the entire project."
# Inconsistent style throughout:
some_var="no spaces" # line 12
other_var = "with spaces" # line 13
very_long_function_call(argument1, argument2, argument3, argument4, argument5) # 120+ charsEinen Formatter waehlen (ruff, black), einmal konfigurieren, nie wieder ueber Stil diskutieren.
KI liebt es, Pakete hinzuzufügen — manchmal unnötig. Jede Abhängigkeit ist ein Risiko. Frage immer, ob eine native Lösung existiert. Weniger Abhängigkeiten = weniger Angriffsfläche.
Nur 8 direkte Abhaengigkeiten sind gepinnt, aber deren transitive Abhaengigkeiten nicht. pip install kann unterschiedliche Versionen auf verschiedenen Maschinen installieren.
Builds sind nicht reproduzierbar. "Works on my machine" wird zum Standard-Problem.
"Install pip-tools (pip install pip-tools). Create requirements.in with direct deps. Run pip-compile requirements.in to generate a fully pinned requirements.txt with hashes."
# requirements.txt — only direct deps pinned:
Flask==2.2.3
openai==1.6.1
SQLAlchemy==2.0.25
# But what version of Jinja2? Markupsafe? Click? Unknown!Immer alle Abhaengigkeiten einschliesslich transitiver pinnen. pip-compile, Poetry oder PDM fuer reproduzierbare Builds verwenden.
Keine Pruefung ob die Lizenzen der 8 Abhaengigkeiten mit dem geplanten Lizenzmodell kompatibel sind.
Falls das Projekt kommerziell vertrieben wird, koennten GPL-lizenzierte Abhaengigkeiten rechtliche Probleme verursachen.
"Run pip-licenses --format=table to audit all dependency licenses. Create LICENSES.md documenting the findings."
# Unknown licenses in dependency tree:
# Flask — BSD-3-Clause (OK)
# openai — Apache-2.0 (OK)
# But what about transitive deps?Abhaengigkeitslizenzen frueh pruefen, besonders bei geplanter kommerzieller Verbreitung.
Flask 2.2.3 ist anfaellig fuer Session-Cookie-Manipulation (CVE-2023-30861). Die aktuelle Version ist 3.1.x.
Angreifer koennen Session-Cookies manipulieren und sich als andere Benutzer ausgeben.
"In requirements.txt, update Flask from 2.2.3 to 3.1.0. Review the Flask 3.0 migration guide for breaking changes. Run tests after update."
# requirements.txt
Flask==2.2.3 # CVE-2023-30861: session cookie vulnerability
Werkzeug==2.2.3 # also outdated, update togetherpip-audit oder safety check regelmaessig ausfuehren. Gepinnte Versionen erfordern aktive Wartung, um sicher zu bleiben.
Das Projekt verwendet setup.py statt pyproject.toml. setup.py wird von PEP 517/518 zugunsten von pyproject.toml abgeloest.
Minimales Risiko kurzfristig, aber zukuenftiges Tooling wird pyproject.toml voraussetzen.
"Convert setup.py to pyproject.toml following PEP 621. Use [build-system] requires = ["setuptools>=68.0"]. Move all metadata to [project] table."
# setup.py (deprecated pattern):
from setuptools import setup
setup(
name="codedictate",
version="0.1.0",
install_requires=[...]
)pyproject.toml fuer neue Python-Projekte verwenden. Es ist der moderne Standard gemaess PEP 517/518/621.
KI-generierter Code enthält oft Sicherheitslücken — hartcodierte Schlüssel, fehlende Validierung, SQL-Verkettung. Diese sind unsichtbar: der Code „funktioniert“, ist aber wie eine offene Haustür. Prüfe bei jedem KI-generierten Code die Eingabevalidierung und ob Geheimnisse im Code stehen.
Der Audio-Upload-Endpunkt akzeptiert jede Datei ohne Pruefung des Dateityps, der Groesse oder des Inhalts.
Angreifer koennen ausfuehrbaren Code hochladen oder den Server durch ueberdimensionierte Dateien ueberlasten.
"Add file validation to the /upload endpoint in server/app.py: check file extension against ALLOWED_EXTENSIONS, enforce MAX_CONTENT_LENGTH, and verify magic bytes."
@app.route("/upload", methods=["POST"])
def upload_audio():
file = request.files["audio"]
file.save(os.path.join(UPLOAD_DIR, file.filename))Datei-Uploads immer validieren: Typ, Groesse und Inhalt pruefen. Dem vom Client bereitgestellten Dateinamen niemals vertrauen.
Der Dateiname wird direkt vom Benutzer uebernommen, ohne secure_filename() oder aehnliche Bereinigung.
Angreifer koennen Dateien in beliebige Verzeichnisse schreiben (z.B. ../../etc/crontab).
"In server/app.py line 71, replace file.filename with secure_filename(file.filename) from werkzeug.utils, or better yet, generate a UUID filename."
file.save(os.path.join(UPLOAD_DIR, file.filename))Vom Benutzer bereitgestellte Dateinamen niemals direkt verwenden. secure_filename() nutzen oder eindeutige Namen serverseitig generieren.
CORS ist mit origins="*" konfiguriert, was Anfragen von jeder beliebigen Domain erlaubt.
Fremde Websites koennen API-Anfragen im Namen authentifizierter Benutzer stellen.
"In server/app.py line 18, replace CORS(app, origins="*") with CORS(app, origins=os.environ.get("ALLOWED_ORIGINS", "http://localhost:3000").split(","))."
CORS(app, origins="*")CORS auf die minimal notwendigen Urspruenge beschraenken. Wildcard-Origins umgehen die Same-Origin-Policy vollstaendig.
Der OpenAI API-Schluessel ist direkt im Quellcode hinterlegt und wird bei jedem Commit ins Repository uebertragen.
Angreifer koennen den API-Schluessel aus dem Git-Verlauf extrahieren und auf Ihre Kosten API-Aufrufe durchfuehren.
"Replace the hardcoded OPENAI_API_KEY in server/config.py with os.environ.get("OPENAI_API_KEY") and add a .env.example file."
OPENAI_API_KEY = "sk-proj-abc123def456ghi789"API-Schluessel und Secrets niemals in die Versionskontrolle committen. Umgebungsvariablen oder einen Secrets-Manager verwenden.
Benutzereingaben werden direkt in eine SQL-Abfrage eingesetzt, ohne Parametrisierung oder Escaping.
Angreifer koennen beliebige SQL-Befehle ausfuehren, Daten stehlen oder die gesamte Datenbank loeschen.
"In server/models.py line 87, replace the f-string SQL query with a parameterized SQLAlchemy query using bindparams or ORM methods."
db.execute(f"SELECT * FROM transcriptions WHERE user_id = '{user_id}' AND title LIKE '%{search}%'")Immer parametrisierte Abfragen verwenden. Benutzereingaben niemals direkt in SQL-Strings einsetzen.
Die Admin-Routen (/admin/users, /admin/stats) sind ohne jegliche Authentifizierung zugaenglich.
Jeder kann Benutzerdaten einsehen, Konten loeschen und Systemeinstellungen aendern.
"Add a @require_admin decorator to all /admin/* routes in server/app.py. Implement JWT-based authentication in server/auth.py."
@app.route("/admin/users")
def admin_users():
users = User.query.all()
return jsonify([u.to_dict() for u in users])Jeder Admin-Endpunkt muss Authentifizierung und Autorisierung erfordern. Verteidigung in der Tiefe bedeutet Pruefung auf jeder Ebene.
Die Anwendung wird mit debug=True gestartet, was den interaktiven Debugger und Code-Reload in der Produktion aktiviert.
Der Werkzeug-Debugger erlaubt Remote Code Execution. Angreifer koennen beliebigen Python-Code auf dem Server ausfuehren.
"In server/app.py line 312, replace app.run(debug=True) with app.run(debug=os.environ.get("FLASK_DEBUG", "0") == "1")."
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)Debug-Modus niemals in Produktion aktivieren. Er offenbart einen interaktiven Debugger, der beliebige Code-Ausfuehrung ermoeglicht.
Keiner der API-Endpunkte hat Rate-Limiting implementiert, weder fuer Authentifizierung noch fuer ressourcenintensive Operationen.
Angreifer koennen Brute-Force-Angriffe durchfuehren oder den Server durch Massenanfragen ueberlasten.
"Add Flask-Limiter to server/app.py. Apply @limiter.limit("5/minute") to auth endpoints and @limiter.limit("10/hour") to /upload."
# No rate limiting configuration found anywhere in the projectRate-Limiting ist fuer alle oeffentlich zugaenglichen APIs unverzichtbar, besonders fuer Auth- und Upload-Endpunkte.
47 Findings in diesem Projekt. Wie viele hat deins?
Einmalig EUR 19 inkl. MwSt. · Kein Abo · Report in 15-45 Min. per E-Mail