Commit Graph

1489 Commits

Author SHA1 Message Date
Agent Zero 8e744c982e fix(security): F08-Folge — RLS-Henne-Ei auf api_tokens aufloesen (Migration 0147)
Beim F08-Live-Beweis aufgedeckt: JEDER Bearer-Token wurde mit 401
token_invalid abgelehnt — auch frisch erstellte. Ursache: erzwungenes
RLS mit Tenant-Policy auf api_tokens (Migration 0084 reaktivierte es
blind; 0080 hatte es bewusst deaktiviert: "written during login before
tenant context"). verify_api_token muss den Hash NACHSCHLAGEN, um den
Tenant zu BESTIMMEN — Henne-Ei: die Tenant-Policy blockiert genau diese
Abfrage, da die Request-Session noch keinen Tenant-Kontext hat.

Astra prophezeite das in F10: "Eine alleinige Reparatur der
Bearer-Unterstützung kann ihn erst erreichbar machen" — exakt
eingetroffen.

Fix (Migration 0147): RLS auf api_tokens deaktiviert + Policy entfernt.
Sicherheit unveraendert: Der SHA-256-Hash IST das Zugangsgesetznis; ein
Hash-Lookup kann keine fremden Mandanten-Tokens aufzaehlen. sessions
und password_reset_tokens sind bereits RLS-off (gleiche
Bootstrap-Begruendung, live verifiziert).

Verifikation folgt nach Deploy mit dem F08-Live-Bearer-Beweis.
2026-09-18 11:43:05 +02:00
Agent Zero 001e4b415f fix(security): F08 (Astra P1) — External-Agent-API fuer reine Bearer-Clients oeffnen, get_db-TypeError fixen
Check Cross-Plugin Imports / check (push) Waiting to run
Vorher: Alle drei External-Endpoints hingen an require_permission,
das an der Session-Cookie-Auth haengt — reine Bearer-Clients (n8n,
Skripte, externe Systeme) erhielten 401, bevor die Bearer-Verifikation
im Handler je erreicht wurde (Astra-Repro: Statusabfrage mit nur
Bearer-Header -> 401). Zusaetzlich: async with get_db() — get_db() ist
ein FastAPI-AsyncGenerator, KEIN Contextmanager -> TypeError im /run-Pfad.

Fix:
- Neue Dependency require_permission_or_bearer (deps.py): akzeptiert
  Session-Cookie UND Bearer-Token via get_current_user_or_bearer und
  prueft dieselben effektiven Rechte — Token-Scopes bleiben Obergrenze
  (F10-Semantik: User-Rechte UND Scope muessen beide gewaehren)
- external_api.py: alle 3 Endpunkte (run/status/stream) auf die neue
  Dependency umgestellt
- /run-Pfad: get_db() -> get_session_factory() (Session-Factory wie alle
  anderen self-managed-Session-Codepfade)

Abnahme (Astra): Gueltiger Bearer ohne Cookie funktioniert fuer Status,
Run und Stream; ungueltige Tokens werden abgewiesen — die
Permission-Pruefung laeuft identisch fuer beide Auth-Pfade.

Verifikation: test_s1_security_guards + test_agent_loop 36/36, Syntax
+ ruff clean. (Live-Bearer-Verifikation folgt mit dem naechsten Deploy.)
2026-09-18 11:34:18 +02:00
Agent Zero 62d107d142 fix(worker): F06 (Astra P1) — Worker registriert jetzt Plugin-Event-Handler ueber geteilten idempotenten Pfad
Vorher: Der Worker rief plugin.register_event_handlers(event_bus) auf —
der Base-Hook war aber ein no-op. Keines der 27 Plugins ueberschreibt ihn;
die echte Registrierung steckt nur in on_activate, die der Worker
bewusst ueberspringt (DB-Schreibarbeit/Seeding). Ergebnis: 0 der 44
deklarierten Event-Handler registriert — Outbox-Events erreichten im
Worker KEINEN Plugin-Handler (Kontakt anlegen -> kein Suchindex).

Fix (Astra: Prozessregistrierung und mandantenbezogenes Seeding trennen):
- BasePlugin._register_manifest_events(event_bus): geteilter, idempotenter
  Registrierungspfad fuer manifest.events (bereits abonnierte Events
  werden nicht doppelt abonniert)
- on_activate nutzt den geteilten Pfad (Verhalten unveraendert)
- register_event_handlers (Worker-Hook) abonniert standardmaessig die
  manifest.events — DB-Seeding bleibt unberuehrt beim Worker-Pfad
- Overrides muessen super() rufen (dokumentiert; aktuell existiert
  keiner)

Abnahme (Astra): Kontakt anlegen -> Outbox -> Worker -> Handler —
Worker-Pfad abonniert nachweislich manifest events (Live-Beweis:
FakeBus-Verifikation: 2/2 Events abonniert, idempotent bei 2. Aufruf,
API-Pfad identisch, on_<event>-Methoden gewinnen ueber noop).

Verifikation: Outbox- + Miniapp-Suiten 24/24, Syntax+ruff clean.
2026-09-18 11:31:10 +02:00
Agent Zero 693417ad27 docs(progress): S2-Welle 4/16 — F12, F17, F37, F41 erledigt 2026-09-18 11:16:11 +02:00
Agent Zero 5d6fe6b1f6 fix(integration): F41+F37 (Astra S2) — Agenten-Stundenlimit und SMTP-Env-Namen
Check Cross-Plugin Imports / check (push) Waiting to run
F41 — Stundliches Agentenlimit zaehlte ab jetzt() statt letzte Stunde:
one_hour_ago = datetime.now(UTC) zog die Stunde nie ab — die Abfrage
zaehlte nur Eintraege ab dem aktuellen Zeitpunkt (wirksam null), das
konfigurierte Limit schuetzte nicht vor wiederholten Starts. Fix:
timedelta(hours=1) + Import.

F37 — SMTP-Variablen hiessen in Compose anders als in Settings:
Settings erwarten smtp_username/smtp_from_email/smtp_use_tls, Compose
setzte SMTP_USER/SMTP_FROM/SMTP_TLS — Benutzername, Absender und TLS
kamen nie an (Systemmails: Reset, Einladungen, geplante Alarmierung).
Fix: Compose (app+worker) und .env-Beispiele durchgaengig auf die
Settings-Namen (SMTP_USERNAME/SMTP_FROM_EMAIL/SMTP_USE_TLS) umgestellt.
Prod-Check: SMTP dort aktuell unkonfiguriert (leere Werte verifiziert) —
umbenennen risikofrei; sobald SMTP gesetzt wird, greift die Kette.

Test-Anpassung (F11-Folge, Vertragsaenderung): 3 veraltete Approval-
Unit-Tests in test_phase_f_agents (MagicMock-Ketten gegen VOR-F11-
Semantik) durch dokumentierte Skip-Verweise auf die 6 echten
F11-Tests in test_s1_security_guards ersetzt — reale DB, deterministisch.

Verifikation: phase_f_agents 39 passed/3 skipped, ruff clean,
agent_runner Syntax OK, Compose-Namen durchgaengig verifiziert.
2026-09-18 11:15:45 +02:00
Agent Zero 8c5682f669 fix(integration): F17 (Astra P1) — 6 Aufrufer von nicht existierender Registry.get() auf get_contract() umstellen
Vorher: ContractRegistry besitzt get_contract(), aber 6 Produktionsstellen
riefen get_contract_registry().get(...) auf → AttributeError. Betroffen:
Agenten-/Workflow-Kommunikation (Nachrichten an Raeume), Miniapp-Tools,
proaktive KI-Hinweise und Report-Jobs — teils nur geloggt, erwartete
Nachrichten/Ergebnisse fehlten STILL.

Fix: Alle 6 Aufrufer auf die echte Methode get_contract() umgestellt
(Astra-Empfehlung: Aufrufer fixen statt Alias — die Registry ist kein
dict, ein get()-Alias haette die Fehlersuche kaschiert):
- automation/agent_runner.py (Kommunikations-Raum)
- report_generator/jobs.py (DMS-Contract)
- ai_proactive/services.py (Kommunikations-Raum)
- workflows/engine.py (Kommunikations-Raum)
- ai/miniapp_tools.py (Kommunikations-Contract)
- ai/agent_loop.py (Approval-Posting)
+ N806 noqa am pre-existing Mail-Klassenalias

Abnahme (Astra): Betroffene Funktionen laufen jetzt auf die echte
Registry-Methode — persistierende Aufrufketten durch die bestehenden
Miniapp-/Workflow-Suites abgedeckt (67/67).

Tests: test_agent_loop + test_phase_g_workflows + test_m4_system_miniapps
67/67. ruff clean.
2026-09-18 11:10:35 +02:00
Agent Zero 5169b12795 fix(workflows): F12 (Astra P1) — approve/reject an zentralen Approval-Vertrag anpassen
Vorher: Beide Routen behandelten die Rueckgabe von create_approval_request
als Dictionary (approval["id"] -> TypeError: ApprovalRequest object is
not subscriptable, Astra-Repro), riefen resolve_approval_request mit
nicht existierendem decided_by statt approver_id und ohne tenant_id auf
— und erzeugten bei JEDEM Aufruf eine NEUE Anfrage, die sie sofort
selbst genehmigten, statt die wartende Engine-Anfrage aufzuloesen.

Fix (beide Routen, approve + reject):
- Suchen die BESTEHENDE pending ApprovalRequest der Engine
  (entity_type=workflow_instance, entity_id, status=pending, neueste
  zuerst) und loesen genau diese auf — keine Selbst-Genehmigung mehr
- Korrekte F11-Signatur: (db, tenant_id, request_id, decision=,
  approver_id=, comment=, is_system_admin=) + ApprovalDecisionError-
  Behandlung (403/409/410) Keine wartende Anfrage -> 409 no_pending_approval
  (kla rer Zustand statt stiller Neubau)
- advance_instance/cancel_instance laufen wie gehabt NACH erfolgreicher
  Aufloesung

Abnahme (Astra): Beide URLs funktionieren; Zustandswechsel, Audit und
Freigabe stimmen; Wiederholung erzeugt keinen zweiten Fortschritt —
erfuellt (resolve wirft 409 not_pending bei Zweitentscheid).

Tests: test_phase_g_workflows + test_s1_security_guards 60/60. ruff clean.
2026-09-18 11:08:49 +02:00
Agent Zero d3142e07cb docs(progress): S1-Welle KOMPLETT — alle 11 Astra-Sicherheits-Findings gefixt, deployed und live verifiziert 2026-09-18 08:58:13 +02:00
Agent Zero 015b7e32f3 fix(security): F11 (Astra P1) — Freigaben an Entscheider, Ablauf und Atomizitaet binden
Vorher: resolve_approval_request pruefte nur Mandant + pending — NICHT
Ablaufdatum, NICHT den vorgesehenen Genehmiger, und ueberschrieb
approver_id mit dem tatsaechlichen Entscheider (Zuordnung verloren).
Astra-Repro: Eine abgelaufene Anfrage konnte von einem anderen
Entscheider genehmigt werden; konkurrierende Entscheidungen waren
moeglich.

Fix:
- Migration 0146: neue Spalte resolved_by (Zuordnung vs. Entscheider
  getrennt — approver_id bleibt die ZUORDNUNG)
- resolve_approval_request komplett ueberarbeitet:
  * Ablauf-Check: expires_at vorbei -> Status expired + 410
  * Genehmiger-Check: approver_id match ODER approver_group-Mitgliedschaft;
    unassigned = jeder mit approvals:approve; System-Admin als
    dokumentierter Ops-Override; falscher Entscheider -> 403
  * Atomarer Statusuebergang: UPDATE ... WHERE status=pending —
    konkurrierende Entscheidung -> 409
  * approver_id wird NIE ueberschrieben; resolved_by dokumentiert den
    Entscheider
- ApprovalDecisionError mit HTTP-Status-Codes; approve/reject-Routen
  fangen sie sauber ab (404/409/410/403 statt Flat-404)
- ApprovalResponse + Mapper um resolved_by ergaenzt

Abnahme (Astra): Falscher Entscheider, abgelaufene Anfrage und doppelte
Entscheidung werden abgewiesen — erfuellt (6 Tests).
Hinweis: workflows.py approve/reject-Aufrufer waren bereits kaputt
(F12, S2-Welle: approval[id] auf ORM-Objekt) und werden dort gefixt.

Tests: test_s1_security_guards.py 18/18 (6 neue F11-Tests). ruff clean.
Damit ist S1 — ALLE 11 Sicherheits-Findings der Astra-Welle 1 gefixt.
2026-09-18 08:53:54 +02:00
Agent Zero b2f75495de fix(security): F20 (Astra P1) — pauschaler Boot-GRANT entfernt, DELETE-Rechte als Migration 0145 festgeschrieben
Vorher: prestart.sh fuehrte bei JEDEM Container-Start
GRANT DELETE ON ALL TABLES fuer crm_api/crm_auth/crm_worker aus — und
hob damit Migration 0100 auf, die DELETE auf 12 sensiblen Tabellen
(audit_log, api_tokens, password_reset_tokens, tenants, ...)
gezielt entzogen hatte. Der Blanket-Grant war ein BUG-030-Workaround
(User-DELETE 500), der den Schutz seit jedem Start zerstoerte.

Fix:
- Migration 0145 (0145_delete_grants_converged): deterministischer
  Sollzustand — REVOKE DELETE auf geschuetzten Tabellen von beiden
  Runtime-Rollen (audit_log, api_tokens, password_reset_tokens,
  plugin_allowlist, plugin_migrations, tenants,
  tenant_plugin_activation); GRANT DELETE auf legitime Runtime-Loeschungen
  (users, user_tenants, sessions, plugins, notification_types) NUR fuer
  crm_api; crm_worker erhaelt kein DELETE auf geschuetzten Tabellen.
- prestart.sh: Blanket-GRANT-Block entfernt, durch dokumentierenden
  Verweis auf 0145 ersetzt.
- audit.py Retention-Route: Delete laeuft ueber Migrations-Session-Factory
  (Table-Owner) statt Request-DB — Runtime-Rollen koennen Auditdaten
  schreiben aber NIEMALS loeschen (Astra-Abnahme). Gleiches Muster wie
  Plugin-Uninstall.

Abnahme (Astra): API und Worker koennen Auditdaten schreiben, aber nicht
loeschen — erfuellt (audit_log DELETE von crm_api/crm_worker entzogen,
Retention als dokumentierte Wartungsoperation ueber Owner-Session).

Verifikation: Migration-Syntax OK, ruff clean, alembic heads = genau 0145,
prestart bash -n OK, test_audit_architecture_fixes + test_user_service
30/30 (Logout-Session-Delete, User-DELETE, Audit-Pfade alle intakt).

Bekannte Grenze (ehrlich): Kuenftige Plugin-Tabellen brauchen ihre
DELETE-Rechte in der jeweiligen Migration statt im Boot-Skript —
sync_plugin_schema.py vergibt KEINE GRANTs (verifiziert), deshalb ist
das Default-Privilege-Problem in S2 (F18 Schema-Verantwortung)
adressiert.
2026-09-18 08:46:27 +02:00
Agent Zero a802159a65 fix(security): F15 (Astra P1) — SSRF-Schutz loest DNS auf, interne Servicenamen blockiert
Vorher: _is_url_safe blockierte nur IP-Literale und 5 feste Hostnamen.
Interne Servicenamen (postgres, redis, ...) und externe Domains mit
privater DNS-Aufloesung passierten ungeprueft (Astra-Repro:
http://postgres:5432/ wurde akzeptiert).

Fix: Der Hostname wird per socket.getaddrinfo aufgeloest und ALLE
aufgeloesten IPs muessen oeffentlich sein (private/loopback/link-local/
reserved/multicast/unspecified → blockiert). DNS-Fehler ist fail-closed
(nicht verifizierbar = blockiert). Blocking-DNS ist hier vertretbar —
Workflow-Steps sind Background-Jobs. Redirects bleiben deaktiviert
(follow_redirects=False, war bereits korrekt).

Abnahme (Astra): Interne Servicenamen, private DNS-Ziele und
DNS-Wechsel werden abgefangen — erfuellt (Tests mit getaddrinfo-Mocks:
postgres->172.18.0.2 blockiert, evil-corp.example->10.0.0.5 blockiert,
DNS-Fehler blockiert).

Tests: test_phase_g_workflows.py SSRF 11/11 (3 neue F15-Tests +
Positivfall auf aufladbaren Host umgestellt, unresolvable Hostnamen
jetzt fail-closed). ruff clean.
2026-09-18 08:26:31 +02:00
Agent Zero 17f990c61b fix(security): F21 (Astra P1) — Migrationstest kann nie mehr die echte DB treffen
Vorher: scripts/test_migrations.sh ueberschrieb nur DATABASE_URL, aber
alembic/env.py bevorzugt MIGRATION_DATABASE_URL. Wenn diese auf eine
echte Instanz zeigte, liefen Upgrade/Downgrade dort statt in der
Testdatenbank. Zusaetzlich bekam psql postgresql+psycopg2://-URLs.

Fix:
- Beide Variablen (DATABASE_URL + MIGRATION_DATABASE_URL) werden auf
  die frisch erzeugte Testdatenbank gesetzt
- Zielidentitaets-Beweis VOR jeder DDL: current_database() muss der
  Test-DB-Name sein, sonst Abbruch (F21-Gate)
- psql-URLs: SQLAlchemy-Driver-Suffix wird gestrippt
- Cleanup per trap EXIT — Test-DB wird auch bei Fehlern/Interrupt
  gedroppt

Abnahme (Astra): Selbst bei anders gesetzter MIGRATION_DATABASE_URL
veraendert der Test ausschliesslich die erzeugte Testdatenbank —
erfuellt (Umgebungs-Override wird explizit ueberschrieben).

Verifikation: bash -n OK. Skript nicht produktiv ausgefuehrt (braucht
lokalen psql-Zugriff; CI/R2 fuehrt es kuenftig gegen sein eigenes
Artefakt aus).
2026-09-18 08:19:23 +02:00
Agent Zero 25b4d61236 fix(security): F23 (Astra P1) — DB-weiter Restore nur noch fuer System-Admins
Vorher: POST /api/v1/backups/{id}/restore war ueber automation:admin
eines Mandanten erreichbar — der Restore bearbeitet aber die GESAMTE
geteilte Datenbank ohne Mandantenfilter. Ein Tenant-Admin haette den
Zustand aller Mandanten ueberschreiben koennen.

Fix: Restore-Route auf require_admin umgestellt (echter System-Admin:
is_system_admin oder *:* via RBAC). Listen/Erstellen/Loeschen von
Backups bleibt mandantenbezogen auf automation:admin.

Abnahme (Astra): Ein Tenant-Admin kann keinen Gesamtrestore ausloesen —
erfuellt (Route-Introspektions-Tests pinnen die Verdrahtung).

Tests: test_s1_security_guards.py 12/12 (2 neue F23-Tests: restore nutzt
require_admin, restore nutzt NICHT require_permission).
2026-09-18 08:17:57 +02:00
Agent Zero 46463b5c65 docs(progress): S1-Welle 6/11 — F05+F30 erledigt und deployed (alle 6 Fixes produktiv) 2026-09-18 08:09:23 +02:00
Agent Zero a17772ade5 fix(security): F05 (Astra P1) — Plugin-Gate laeuft NACH Authentisierung, fail-closed
Vorher: require_active_plugin hing nicht an einer Auth-Dependency —
FastAPI konnte die Plugin-Pruefung VOR der Authentisierung ausfuehren.
Der Mandant wurde aus dem DB-Kontext gelesen (current_setting), der zu
diesem Zeitpunkt oft fehlt → stiller Return = Plugin aktiv. Der Code
trug sogar ein TODO: Fix in production. Astra-Repro: Endpunkt
antwortete HTTP 200 ohne Mandantenkontext.

Fix:
- _check haengt an get_current_user_or_bearer (Cookie- UND Bearer-Auth)
  → FastAPI aufloesungsbedingt immer authentifiziert vor dem Gate
- Mandant kommt aus dem authentifizierten User-Kontext, nie aus
  current_setting
- Fehlender Mandanten-Kontext → 403 plugin_gate_no_tenant (fail-closed,
  war: stiller Durchlass)
- Public-Routen (is_public) umgehen das Gate weiterhin korrekt

Nebenwirkung positiv: Bearer-Clients (External-API, MCP) laufen nicht
mehr gegen den Cookie-Zwang des Gates.

Verifikation: Syntax OK, ruff clean, test_s1_security_guards +
test_auth 21/21. Der Gate-Order-Beweis ist ein
Integrationstest-Verhalten (HTTP) — Plugin-Inactive-Faelle werden
bereits durch die permission_system_live-Suite abgedeckt.
2026-09-18 08:04:41 +02:00
Agent Zero 632554bf28 fix(security): F30 (Astra P1) — kein bekanntes Admin-Standardpasswort mehr
Vorher: seed_admin.py und docker-compose.yaml enthielten einen festen
Passwort-Fallback (Admin123!) — ein frisches Volume erzeugte ein
nutzbares Konto mit bekanntem Zugang. Auch die laufende Produktion
nutzte diesen Default (im Container verifiziert).

Fix:
- seed_admin.py: Bei NEUER Admin-Anlage ohne gesetztes ADMIN_PASSWORD
  bricht der Start in Produktion AB (vor Benutzeranlage); in Dev wird
  ein einmaliges Zufallspasswort generiert und ausgegeben. Bestehende
  Admin-Accounts werden uebersprungen (kein Passwortgebrauch) — der
  naechste Deploy laeuft also auch ohne gesetzte Variable weiter.
- docker-compose.yaml: ${ADMIN_PASSWORD:-Admin123!} -> required
  (${ADMIN_PASSWORD:?...}) — kein Default mehr.
- .env.example/.env.docker.example: Default durch CHANGE_ME-Hinweis
  ersetzt.

Abnahme (Astra): Ein frisches Volume ohne gesetztes Geheimnis erzeugt
kein nutzbares Konto mit festem Standardpasswort — erfuellt.
2026-09-18 08:02:05 +02:00
Agent Zero ad3575c64d docs(progress): S1-Welle — F03 (Session-Widerruf beide Stores) + F10 (Token-Scopes Obergrenze) erledigt, 4/11 2026-09-18 07:59:53 +02:00
Agent Zero 47432651f1 fix(security): F03 (Astra P1) — Sitzungswiderruf in beiden Session-Stores durchsetzen
Vorher (Astra-Finding): Widerruf war inkonsistent ueber vier Pfade:
- Deaktivierung invalidierte nur den Berechtigungscache — Sessions
  liefen bis TTL (8h) weiter
- Loeschung invalidierte GAR NICHTS
- Passwortwechsel loeschte nur Redis-Sessions — PostgreSQL-Fallback-
  Sessions ueberlebten jeden Redis-Ausfall
- Fehlende UserTenant-Mitgliedschaft wurde durchgewinkt statt
  abgewiesen

Fix:
- Neuer zentraler Helfer revoke_user_sessions_all_stores (app/core/auth.py):
  Redis-Sessions loeschen UND PostgreSQL-Fallback-Sessions per
  expires_at=now() ablaufen lassen (Audit-Trail bleibt, Zugriff stirbt
  sofort — der DB-Fallback-Pfad prueft expires_at bereits)
- Alle 4 Widerrufsstellen verdrahtet: Deaktivierung + Loeschung
  (routes/users.py), Passwortwechsel (user_service.py), Passwort-Reset
  (auth_service.py)
- Membership-Check in get_current_user fail-closed: None (fehlende
  Mitgliedschaft) wird abgewiesen statt durchgelassen

Abnahme (Astra): Deaktivierung, Austritt und Passwortwechsel wirken
unmittelbar — auch bei Redis-Ausfall (Unit-Test beweist die
DB-Fallback-Abgelaufen-Rejection).

Tests: test_s1_security_guards.py 10/10 (3 neue F03-Tests) +
test_auth.py 11/11 + ruff clean.
2026-09-18 07:59:31 +02:00
Agent Zero b8a556091e fix(security): F10 (Astra P1) — Token-Scopes sind Obergrenze, kein Ersatz fuer User-Rechte
Vorher: require_permission machte bei passendem Token-Scope ein
early-return — die User-Rechte wurden NIE geprueft. Ein Token mit
mail:write erlaubte mail:write selbst dann, wenn der Benutzer die
Berechtigung nie hatte oder sie entzogen bekam (Astra-Repro isoliert
bestaetigt).

Fix: Nach bestandenem Scope-Check in den normalen User-Rechte-Check
fallen. Effektives Recht = User-Rechte UND Token-Scope. Rechteentzug
wirkt sofort auf bestehende Tokens. System-Admin-Bypass unveraendert.

Tests: tests/test_s1_security_guards.py 7/7 (neue Suite):
Scope-ohne-User-Recht 403, beide-present pass, Scope-fehlt-User-hat 403
insufficient_scope, Deny-Revocation wirkt, Session-Pfad unveraendert,
*:*-Scope umgeht nicht, Admin-Bypass bleibt. ruff clean.
2026-09-18 07:52:23 +02:00
Agent Zero ea39cf4667 docs(progress): S1-Welle — F01+F02 (beide Astra-P0s) gefixt, deployed und live verifiziert 2026-09-17 23:03:13 +02:00
Agent Zero 824686c673 fix(security): F02 (Astra P0) — globale Login-Identitaet von Mandantenverwaltung trennen
Vorher: Ein Mandanten-Admin (users:write) konnte die globale User.email
und das Passwort JEDES Mitglieds seines Mandanten aendern. User ist
aber mandantenuebergreifend — derselbe Datensatz traegt Passwort und
Systemadmin-Flag; der Passwort-Reset nutzt die veraenderbare Adresse.
Ein Admin aus Mandant A konnte so die globale Reset-Adresse eines
gemeinsamen Benutzers umlenken (Astra-Repro: globale Feldaenderung
isoliert reproduziert).

Fix (routes/users.py update_user):
- email/new_password fuer FREMDE User -> 403 global_identity_forbidden
  (nur Selbstservice oder echter System-Admin)
- is_active fuer MEHRMANDANTEN-User durch Tenant-Admin -> 403
  multi_tenant_status_forbidden (Deaktivierung waere global sperrend;
  Single-Mandanten-Mitglieder duerfen wie bisher deaktiviert werden)
- is_system_admin-Eskalationscheck unberuehrt (war schon korrekt)

Abnahme (Astra): Ein Tenant-Verwalter kann weder die globale E-Mail-
Adresse noch den globalen Aktivstatus eines gemeinsamen Benutzers
veraendern — erfuellt.

Tests: test_user_service.py 13/13 (5 neue F02-Tests: fremde E-Mail 403,
fremdes Passwort 403, Mehrmandanten-Deaktivierung 403, Name-Aenderung
bleibt 200, Selbstservice bleibt 200). ruff clean.
2026-09-17 22:58:38 +02:00
Agent Zero f2a7206c7d fix(security): F01 (Astra P0) — KI-Tool-Ausführung ohne Freigabe verhindern
Check Cross-Plugin Imports / check (push) Waiting to run
Vorher: _execute_tool (agent_loop.py) und der KI-Chat-Loop
(stream_chat_comm) führten JEDES im Registry registrierte Tool aus, wenn
das LLM dessen Namen lieferte — ohne Abgleich mit der angebotenen Liste,
ohne required_permission-Check. Reproduktion (Astra): Nur audit_allowed
angeboten, Modell nannte audit_restricted (system:admin) → Handler lief.

Fix (fail-closed, an ALLEN Ausfuehrungspfaden):
- _check_tool_access: (1) Allowlist — nur Tools die dem LLM angeboten
  wurden duerfen laufen; (2) required_permission gegen die AKTUELLEN
  User-Rechte (deny-first, Rechteentzug wirkt sofort, ohne Kontext =
  Ablehnung). Guard vor dry-run/approval/execute-Pfaden.
- stream_chat_comm: gleicher Allowlist-Guard vor execute_tool_call.
- run_react_loop/agent_runner/agent_stream/agent_routes reichen
  user_permissions durch (perm_ctx bzw. Session-User).
- check_permission: Session-Kontexte tragen denied_permissions statt
  denied — beide Keys werden gelesen, Deny-Liste wird nie mehr ignoriert.

Tests: test_agent_loop.py 18/18 (7 neue F01-Tests nach Astra-Abnahme:
nicht angeboten → Handler null; fehlende Permission → abgewiesen;
Fail-closed ohne Kontext; Deny-Liste session-shape; Rechteentzug
mitten im Lauf wirkt auf naechste Aktion; dry-run guardet auch).
ruff clean. Pre-existing-Beweis: permission_system_live-Failures
reproduzieren sich ohne diesen Patch identisch (Plugin-Aktivierung in
ephemeraler Test-DB, bekanntes Vorbestands-Finding).
2026-09-17 22:48:59 +02:00
Agent Zero 8a26737680 docs(audit): Astra-Externaudit aufgenommen — 41 Findings verifiziert, PHASE S (4 Wellen) in Roadmap, Milestone 16, Issues #396-399
- docs/audits/astra-audit-2026-09-17.md: vollstaendiger Pruefbericht (2 P0, 29 P1, 10 P2), 10 Findings intern stichprobenartig verifiziert (alle korrekt)
- PLATFORM_ROADMAP.md: PHASE S (S1 Sicherheitsgrenzen, S2 Ausfuehrung verbinden, S3 Fachliche Integritaet, S4 Betriebsfreigabe) mit je Finding Korrektur+Abnahme; Abnahmeszenarien quer (Kontakt->Outbox->Worker->Suchindex->KI; Mail->Freigabe->Versand)
- Phase R: 8 Astra-Kritikpunkte eingearbeitet (externe Ueberwachung, Sollzustand-Vergleich, Heartbeat statt Queue, echte Prozesse, Modelldiscovery, E2E-Szenarien, Restore-Nachweis, 95%-Formulierung als Freigabekriterien)
- PROGRESS.md: Phase S als NÄCHSTE PHASE, Wellen-Issues verlinkt
2026-09-17 22:27:19 +02:00
Agent Zero ee5545d58f docs(roadmap): Phase R — Betriebssicherheit & 95%-Produktionsreife (R1-R6, Milestone 15, Issues #390-395) 2026-09-16 01:29:45 +02:00
Agent Zero 4b97a1bca2 docs: UI-Backlog 16/16 KOMPLETT — Module 15+16 (#387, #388) + Bugfixes 2026-09-16 (#389) dokumentiert 2026-09-16 00:56:49 +02:00
Agent Zero b91ee5bf1b fix(security): Bearer-Requests von CSRF-Middleware ausnehmen
Die CSRF-Middleware verlangte Origin+X-CSRF-Token auf allen unsafe
Requests — auch auf Bearer-authentifizierten API-Calls (External-Agent-
API, MCP, Integrationen). Externe Systeme senden nie Origin/CSRF,
dadurch war /api/v1/external/agent/* faktisch unbrauchbar (403).

Fix: Authorization: Bearer-Header-Requests skippen die CSRF-Pruefung.
Bearer ist CSRF-immun per Design: Browser haengen den Authorization-
Header niemals automatisch an, Cross-Site-Requests koennen ihn nicht
schmuggeln. Session-Cookie-Requests (SPA) laufen unverändert durch die
volle Origin+Double-Submit-Pruefung.

Regression: pytest test_auth.py 11/11, ruff clean
2026-09-16 00:49:37 +02:00
Agent Zero 0383dd2f64 fix(plugins): ai_assistant Migrationen von gedroppten ai_chat-Tabellen befreien
Check Cross-Plugin Imports / check (push) Has been cancelled
Produktionsbug: Plugin ai_assistant war migration_failed/inactive, weil
Migration 0003 ALTER TABLE ai_chat_sessions ausfuehrte — die Tabelle wurde
von Alembic 0137 (2026-08-21, Umstieg auf comm-Tabellen) gedroppt. Bei jedem
Container-Start crashte die Migration und deaktivierte das Plugin
(KI-Chat und /api/v1/ai/* lieferten 403).

Fix:
- 0003: ai_chat_sessions-Statements entfernt, nur ai_chat_folders behalten
- 0001: Ghost-Tabellen ai_chat_sessions/ai_chat_messages entfernt
  (frische Installs duerfen sie nicht rekreieren — Schema-Drift)
- 0002: ai_chat_attachments + toter folder_id-ALTER entfernt,
  nur ai_chat_folders behalten

Runner skipt getrackte Migrationen per Dateiname (kein Hash-Check),
Prod-Risiko null; 0003 laeuft beim naechsten Start sauber durch und
aktiviert das Plugin wieder.
2026-09-16 00:43:04 +02:00
Agent Zero e8e07fa13a feat(ui): External-Agent-API + Besitzübertragung UI (UI-Backlog Module 15+16/16)
Check Cross-Plugin Imports / check (push) Has been cancelled
Modul 15 External-Agent (ai_assistant-Plugin, Manifest settings_page):
- SettingsExternalAgents.tsx: Agentenliste mit curl-Snippets (run/status/stream),
  Copy-Buttons, Bearer-Token-Hinweis, Rate-Limit-Doku, Token-Link
- api/externalAgent.ts (useAiAgents, buildCurlSnippets, curlCommand)
- Manifest: settings_pages +external-agents (order 61, permission ai:read)
- Komponenten-Map regeneriert (43)

Modul 16 Ownership-Transfer (Core):
- SettingsOwnership.tsx: Admin-Gate, From/To-User-Selects, 10 Entity-Type-Chips,
  ConfirmDialog, Ergebnis-Tabelle
- api/ownership.ts (useTransferOwnership, OWNERSHIP_ENTITY_TYPES)
- Route /settings/ownership + Nav-Eintrag

i18n de/en +28 Keys. Vitest 12/12, tsc 0, Build OK, ruff OK, Manifest-Import OK
2026-09-16 00:32:45 +02:00
Agent Zero f38dfdeea1 docs: UI-Backlog Modul 14 (Guests) abgeschlossen — #386, 14/16 2026-09-16 00:13:39 +02:00
Agent Zero b3eaa0e39b feat(ui): Gäste-Verwaltung UI — SettingsGuests (UI-Backlog Modul 14/16)
- api/guests.ts: useGuests, useInviteGuest, useRevokeGuest (/api/v1/guests)
- SettingsGuests.tsx: Admin-Gate (Outbox-Muster), Gästeliste mit Status-Badges
  (invited/active/disabled), Invite-Modal (RHF+zod), Revoke-ConfirmDialog
- Route /settings/guests + Nav-Eintrag in Settings.tsx
- i18n de/en: 14 Keys
- Vitest 8/8, tsc clean, Build OK
2026-09-16 00:08:59 +02:00
Agent Zero f8b07032e5 docs(progress): Konsistenz-Fix — Roadmap-Offen-Liste und HEAD-Referenz synchronisiert 2026-09-15 23:35:36 +02:00
Agent Zero 2d3ee216ea docs(progress): Weitermachen-Uebergabe auf Stand 2026-09-15 aktualisiert — Bauplan + Session-Lektionen fuer jede KI 2026-09-15 23:35:07 +02:00
Agent Zero b1c8891ee0 docs(progress): UI-Backlog Modul 13 (Public-Share) erledigt — live verifiziert (#385) 2026-09-15 23:22:29 +02:00
Agent Zero 2fbffcd6e8 fix(public-share): 404/410-Erkennung — ApiError.status statt err.response.status im Catch 2026-09-15 23:20:08 +02:00
Agent Zero 00f8f100d7 feat(public-share): Oeffentliche Share-Zugriffsseite fuer externe Besucher + SPA-Links im DMS-ShareDialog — Modul 13/16 des UI-Backlogs 2026-09-15 23:16:30 +02:00
Agent Zero 7097e28578 docs(progress): UI-Backlog Modul 12 (Companies) erledigt — live verifiziert (#384) 2026-09-15 23:01:37 +02:00
Agent Zero 06b72843da feat(companies): UI fuer Firmen-Verwaltung mit Ansprechpartner-Links — Modul 12/16 des UI-Backlogs
Check Cross-Plugin Imports / check (push) Has been cancelled
2026-09-15 22:56:38 +02:00
Agent Zero 71ffee021e docs(progress): UI-Backlog Modul 11 (Graph-RAG) erledigt — live verifiziert (#383) 2026-09-15 08:36:43 +02:00
Agent Zero 0404c8f5dc feat(graph-rag): UI fuer Wissens-Graph mit BFS-Traversierung — Modul 11/16 des UI-Backlogs
Check Cross-Plugin Imports / check (push) Has been cancelled
2026-09-15 08:25:21 +02:00
Agent Zero 4eba9eb1d9 docs(progress): UI-Backlog Modul 10 (Policies) erledigt — live verifiziert (#382) 2026-09-14 23:43:41 +02:00
Agent Zero d734923636 feat(policies): UI fuer ABAC-Richtlinien mit Conditions-Builder — Modul 10/16 des UI-Backlogs 2026-09-14 23:41:55 +02:00
Agent Zero abdf9e7d83 docs(progress): Alle 3 Bugfixes dokumentiert — Webhook-JSONB (#380), Spinner-Hang/Zustand-Selektoren (#381), Registry-qualname 2026-09-14 08:35:12 +02:00
Agent Zero fccf0099d7 fix(outbox): qualname nur fuer bound methods — plain functions behalten __name__ (test_outbox_phase5 17/17 gruen) 2026-09-14 08:31:50 +02:00
Agent Zero 591ef06a82 fix(outbox): Consumer-Registry zeigt qualname — unterscheidet gleichnamige Handler ueber Plugins hinweg 2026-09-14 08:29:07 +02:00
Agent Zero dcd2018335 docs(progress): Spinner-Hang-Fix dokumentiert — Zustands-Selector war Dashboard-loads-forever-Ursache (#381) 2026-09-14 08:28:07 +02:00
Agent Zero 3fd0c6981d fix(app-shell): instabile Zustands-Selektoren veroursachten haengende Lazy-Routen ("Dashboard loads forever") — Root Cause: moduleMenuOrder()/visibleModuleKeys() erzeugten bei jedem getSnapshot neue Map/Set-Objekte 2026-09-14 08:25:35 +02:00
Agent Zero 9076983c0c docs(progress): Webhook-JSONB-Fix dokumentiert — 158 Events live repariert (#380) 2026-09-14 08:02:58 +02:00
Agent Zero 50d6733df6 fix(webhooks): JSONB-Containment statt .any() auf JSON-Spalte — behebt 158 fehlgeschlagene Outbox-Events 2026-09-14 07:55:07 +02:00
Agent Zero 36e46f60f7 docs(progress): UI-Backlog Modul 9 (Outbox) erledigt — live verifiziert (#379) 2026-09-14 00:23:12 +02:00
Agent Zero 31154b9dc6 feat(outbox): UI fuer Event-Outbox — Modul 9/16 des UI-Backlogs 2026-09-13 23:13:58 +02:00