Deep Analysis 2026-06-04 — teddytafforge-proxmox #9

Closed
opened 2026-06-04 01:45:10 +02:00 by vr6syncro · 1 comment
Owner

Deep Analysis 2026-06-04 — teddytafforge-proxmox

Autonome Multi-Agent-Analyse (Recon + 5 Blickrichtungen + adversariale Verifikation). Stand 2026-06-04.

1. Ueberblick & Einordnung

  • repo_class: full — reiner Bash/POSIX-sh Installations-Wrapper, kein Anwendungscode.
  • Zweck: Ein-Kommando-Deploy von TafForge (Custom-Tonie-.taf-Builder) und optional TeddyCloud als unprivilegierter Debian-13-LXC auf Proxmox VE. Topologien: sidecar (TC extern, mp0-Bind-Mount) und all-in-one (TC nativ im selben LXC gebaut).
  • Stack: Host-Entrypoint ct/teddytafforge.sh → misc/build.func (+install.func) → In-LXC-Installer install/*.sh → verbatim vendored Upstream-Installer vendor/tafforge-install/. systemd-Units, whiptail-Dialoge, DRY_RUN-Matrix-Dry-Test, 3-stufige Forgejo-CI (shellcheck/bashate, bash -n, Drift-Probe + URL-Liveness).
  • Code-Qualitaet der Shell-Logik ueberdurchschnittlich: pct-Argumente via Bash-Arrays (keine Command-Injection), Eingabe-Validatoren, ERR-Traps mit Stack-Trace, Rollback-Traps fuer halb-gebaute LXCs, gehaertete TafForge-systemd-Unit.

Wichtige Drift-Korrektur (alle 5 Agenten bestaetigt + adversarial verifiziert): Der lokale Klon steht auf Branch fix/followup-2026-05-28 (HEAD 5799d17 = offener PR #7), nicht auf main (aad7f31). git merge-base --is-ancestor 5799d17 origin/main => NEIN. Der Versions-Pin v0.2.2 existiert nur im PR-Branch; origin/main traegt weiterhin den mutable main-Ref (verifiziert via git show origin/main:). Wer das public Repo per curl|bash nutzt, zieht den ungepinnten Stand. Befunde sind daher gegen origin/main als Ground-Truth formuliert.

2. Gesamtbewertung

Health-Grade: B

Strukturell sauberes, sorgfaeltig gebautes Wrapper-Repo mit guter Defensive (Array-basierte pct-Aufrufe, Traps, Validatoren, gehaertete TafForge-Unit, Matrix-Dry-Test, mehrstufige CI). Keine committeten Secrets (.claude/settings.local.json = 0 Bytes; .gitignore deckt .env/*.key/*.pem). Die offenen Punkte sind real, aber begrenzt: das dominierende Thema ist das Supply-Chain-/Ausfuehrungsmodell (curl|bash als root ohne Integritaetspruefung + mutable App-Ref auf main), dazu zwei vollstaendig tote IPv6-Pfade und eine Update-Pfad-Regression, die der geplante Tag-Pin (PR #7) einfuehrt. Kein einzelnes Critical. B statt A wegen der ungeloesten Supply-Chain-Lage und der Tatsache, dass der einzige fertige Fix (PR #7) seit 2026-05-29 ungemergt haengt — und so wie er ist den Updater bricht.

3. Bugs & Korrektheit

  • [MEDIUM] git pull --ff-only wird nach Tag-Klon zum stillen No-Op — Update-Pfad bricht durch den v0.2.2-Pin — vendor/tafforge-install/lxc/update-app.sh:50-52, install/teddytafforge-install.sh:41,83-86 — Der In-LXC-Installer klont die App mit git clone --depth=1 --branch "${TAFFORGE_REF}" und kopiert .git nach ${APP_DIR}/.git, damit update-app.sh den git-Pfad nimmt. Sobald TAFFORGE_REF ein Tag ist (PR #7: v0.2.2), erzeugt der Tag-Klon einen detached HEAD ohne Tracking-Branch; git pull --ff-only meldet dann immer „Bereits aktuell" bzw. schlaegt fehl — der App-Code bleibt fuer immer auf v0.2.2, nur pip/npm laufen. Auf main (Branch-Klon) funktioniert der Updater; der Tag-Pin aus PR #7 fuehrt die Regression erst ein. Empfehlung: update-app.sh ref-bewusst machen — bei detached/Tag-Checkout git fetch origin "$APP_REPO_REF" && git checkout "$APP_REPO_REF" statt blindem pull --ff-only; mindestens detached HEAD erkennen und ehrlich melden statt stillem No-Op. (Voraussetzung bevor PR #7 / Tag-Pins ausgeliefert werden; bezieht sich auf #3 / PR #7.)

  • [MEDIUM] disable_ipv6() ist toter Code — wird nie aufgerufen — misc/install.func:47-54 — Die Funktion schreibt per sysctl /etc/sysctl.d/99-disable-ipv6.conf, wenn DISABLE_IPV6=yes, wird aber in keinem Skript der Kette aufgerufen (grep -rn 'disable_ipv6' zeigt nur die Definition). Die whiptail-Frage „Disable IPv6 in the LXC? (Recommended on networks without IPv6 to avoid apt hangs.)" (build.func:354) suggeriert eine In-LXC-IPv6-Abschaltung, die faktisch nie passiert — DISABLE_IPV6 wirkt nur auf build.func:525 (,ip6=auto im net_arg weglassen), was den IPv6-Stack im LXC nicht deaktiviert; der versprochene apt-Hang-Schutz greift nicht. Empfehlung: disable_ipv6 in setting_up_container() (oder am Anfang der In-LXC-Installer) aufrufen ODER Funktion + irrefuehrende Dialogzusage entfernen.

  • [LOW] DISABLE_IPV6 wird nicht an den In-LXC-Installer durchgereicht — misc/build.func:605-616, misc/install.func:48 — Selbst wenn disable_ipv6() verdrahtet waere, koennte es nie greifen: der pct exec ... -- env-Block (605-616) listet DISABLE_IPV6 nicht auf. Die In-LXC-Skripte sehen die Variable nie; ${DISABLE_IPV6:-no} wertet immer zu no. Zwei unabhaengige Defekte (nie aufgerufen + nie uebergeben) sorgen gemeinsam dafuer, dass die In-LXC-Abschaltung unter keinen Umstaenden laeuft. Empfehlung: DISABLE_IPV6="$DISABLE_IPV6" in den pct exec env-Block aufnehmen (zusammen mit dem Aufruf aus dem vorigen Finding).

  • [LOW] Sidecar-Update re-templatet das Plugin auf den all-in-one-Default-Pfad — vendor/tafforge-install/lxc/update-app.sh:87, tafforge.env.sample:17,20, misc/build.func:417 — update-app.sh ruft install-plugin.sh mit "${PLUGIN_ROOT:-/opt/teddycloud/data/www/plugins}" (all-in-one-Layout). Im Sidecar liegt das Ziel unter dem mp0-Mount (Default /mnt/teddycloud-data/.../plugins). PLUGIN_ROOT ist in tafforge.env.sample auskommentiert, TEDDYCLOUD_DATA_DIR=/opt/teddycloud/data fest gesetzt; beim Sidecar-Update wird PLUGIN_ROOT nicht gesetzt → Default greift → install-plugin.sh laeuft gegen einen im Sidecar nicht existierenden Pfad und ueberspringt (per || true, kein Abbruch) die Aktualisierung. Erstinstallation ist korrekt (Wrapper uebergibt dort den mp0-Pfad); nur der spaetere Update-Lauf verliert diesen Kontext. Empfehlung: Bei Wrapper-Installs den real gewaehlten mp0-Pfad als PLUGIN_ROOT/TEDDYCLOUD_DATA_DIR in /etc/tafforge/tafforge.env persistieren, oder in update-app.sh aus /etc/tafforge/proxmox.meta ableiten.

  • [LOW] valid_ipv4/valid_cidr akzeptieren 5+ Oktette (z.B. 1.2.3.4.5) — misc/build.func:216,223 — Beide nutzen IFS=. read -r a b c d _ <<< "$addr" und pruefen nur [ -n "$d" ]. Ein fuenftes Feld landet in _ und wird nicht zurueckgewiesen. Empirisch reproduziert: valid_ipv4 '1.2.3.4.5' => return 0, valid_cidr '1.2.3.4.5/24' => return 0; zu kurze Adressen (1.2.3) werden korrekt abgewiesen, zu lange nicht. Geringe Auswirkung (Wert geht in pct ... ip=, das selbst validiert), aber die Validierungsfunktion erfuellt ihren Zweck nicht vollstaendig. Empfehlung: Trailing-Feld pruefen: IFS=. read -r a b c d e <<< "$addr"; [ -n "$d" ] && [ -z "$e" ] || return 1.

4. Security

Oeffentliches Repo — Details verallgemeinert, keine Exploit-Schritte.

  • [HIGH] Mehrstufiges curl|bash/source <(curl …) als root ohne Integritaetspruefung (keine Checksum/Signatur/Commit-Pin) — ct/teddytafforge.sh:15,19, misc/build.func:605-616, install/all-in-one-install.sh:14,37,41, install/teddycloud-install.sh — Jede Installer-Ebene bootstrappt fremden Code ohne GPG-Signatur, sha256-Vergleich oder Commit-Pinning. REPO_RAW_BASE zeigt hartkodiert auf /raw/branch/main (beweglicher Branch). build.func laeuft nach root_check als root und fuehrt via pct exec ... bash -c "curl … | bash" weiteren Code in der LXC aus; TLS schuetzt nur den Transport, nicht die Integritaet des Inhalts. Eine Kompromittierung von Forgejo/GitHub, ein boesartiger Commit/PR-Merge oder eine TLS-Interception (rogue CA) ergibt root-Codeausfuehrung auf dem PVE-Host. Status: als Forgejo-Issue #2 getrackt, am 2026-05-29 als bewusste Operator-Risikoakzeptanz geschlossen (Repo hat keine Tags / keine Release-Pipeline). Teil-Minderung vorhanden: README enthaelt eine „Paranoid alternative" (download → less → run, README.md:35-36) und einen „security trade-off"-Hinweis (README.md:225) — entgegen einem Agenten-Befund existiert ein Inspect-Pfad. Es fehlt aber weiterhin jede Pin-/Checksum-/Signatur-Massnahme. Empfehlung: REPO_RAW_BASE auf einen unveraenderlichen Ref (Tag/Commit-SHA) pinnen und per-curl-Artefakte gegen ein eingechecktes sha256-Manifest/Signatur verifizieren; Release-/Tag-Prozess fuer das Wrapper-Repo etablieren. (Bereits getrackt als #2 — Restrisiko bleibt; nicht als neu darstellen.)

  • [HIGH] Default-Branch main klont die TafForge-App weiterhin vom beweglichen main-Ref (mutable Supply-Chain-Ref) — misc/build.func:24 (origin/main), install/teddytafforge-install.sh:21 (origin/main), vendor/tafforge-install/lxc/install.sh (git reset --hard origin/$APP_REPO_REF), update-app.sh:52 — Die App laeuft als (Service-User-)uvicorn-systemd-Dienst und wird per git clone --branch "${TAFFORGE_REF}" geklont. Auf origin/main verifiziert: TAFFORGE_REF_DEFAULT="${TAFFORGE_REF_DEFAULT:-main}" (build.func:24) und TAFFORGE_REF="${TAFFORGE_REF:-main}" (teddytafforge-install.sh:21). Der Pin v0.2.2 existiert nur im offenen PR #7 (Closes #3, mergeable=true), ist also nicht ausgeliefert. TeddyCloud ist dagegen auf Tag tc_v0.6.8 gepinnt (build.func:26) — die Inkonsistenz unterstreicht die fehlende App-Pinnung. Empfehlung: PR #7 nach main mergen — aber erst nach dem Update-Pfad-Fix (Finding 3.1), sonst tauscht man den mutable Ref gegen einen toten Updater; zusaetzlich den geklonten Tree gegen einen erwarteten Commit-SHA verifizieren. (Bezieht sich auf Issue #3 / PR #7.)

  • [MEDIUM] TafForge-Web-UI bindet auf 0.0.0.0:${TAFFORGE_PORT} (Default 3000) ohne vom Wrapper eingerichtete Authentifizierung — vendor/tafforge-install/lxc/tafforge.service:13, vendor/tafforge-install/lib/env-defaults.sh:19,31,37, misc/build.func:641 — Die systemd-Unit startet uvicorn mit --host 0.0.0.0; der Wrapper richtet keinerlei Auth, Reverse-Proxy-Auth oder Firewall ein und bewirbt die UI offen unter http://<lxc-ip>:3000/. TafForge bietet Datei-Upload (MAX_UPLOAD_BYTES=2 GiB) und yt-dlp-Outbound-Fetch (ALLOW_NON_YOUTUBE_SOURCES=true per Default) — d.h. ein per Default LAN-weit erreichbarer Dienst mit Upload-/Fetch-Oberflaeche. Ob die App selbst AuthN/AuthZ hat, ist hier nicht pruefbar (App-Code liegt extern im GitHub-Mirror). Empfehlung: Im README klar dokumentieren, dass der Dienst ohne Auth LAN-weit erreichbar ist und hinter authentifizierenden Reverse-Proxy/VPN gehoert; optional 127.0.0.1-Bind bzw. konfigurierbares Bind-Interface als sicherere Default-Variante und ALLOW_NON_YOUTUBE_SOURCES-Default ueberdenken.

  • [MEDIUM] TeddyCloud-Dienst laeuft als root ohne systemd-Hardening; Hardening-Gefaelle gegenueber der TafForge-Unit — install/teddycloud-install.sh:120-136, :73, :143 — Im all-in-one-Pfad wird TeddyCloud nativ als root gebaut (make … zip) und die erzeugte teddycloud.service laeuft ohne User=/Group= und ohne jegliche Protect*/NoNewPrivileges-Direktiven (= root), waehrend tafforge.service vorbildlich gehaertet ist (User=tafforge, NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp …). TC bindet Admin-Ports 80/443/8443 ohne Wrapper-seitiges Auth/Firewalling. Mitigiert durch den unprivilegierten LXC (build.func --unprivileged 1) — ein root-Kompromiss im Container ist nicht automatisch root auf dem Host. Empfehlung: teddycloud.service mindestens mit NoNewPrivileges=true, ProtectHome, PrivateTmp versehen; falls TC einen Service-User toleriert, User=+ReadWritePaths setzen und AmbientCapabilities=CAP_NET_BIND_SERVICE statt vollem root fuer Ports <1024. Im README dokumentieren, dass TC-Admin ohne Auth erreichbar ist.

  • [LOW] TEMP-Debug/xtrace-Block schreibt Trace nach vorhersehbarem /tmp-Pfad (kein mktemp) — misc/build.func:48-53,603, ct/teddytafforge.sh:23-29 — Mit DEBUG=1 wird ein voller bash -x-Trace nach /tmp/teddytafforge-trace-$$.log geschrieben; build.func:603 legt zusaetzlich /tmp/tafforge-install-${CTID}.log ohne explizite Permissions/mktemp an. Pfade sind vorhersehbar (PID/CTID), was auf einem Mehrbenutzer-PVE-Host theoretisch Symlink-Risiken erlaubt; praktisch laeuft der Host-Teil nur als root und der Trace nutzt einen dedizierten FD 19. Je nach ENV-Werten koennen Betriebsdaten in den /tmp-Log gelangen. Empfehlung: Block vor breiter Auslieferung entfernen (von den Autoren selbst als TEMP markiert) oder hinter explizites Opt-in mit install -m 0600/mktemp-Log absichern.

5. Features & QOL

  • [MEDIUM] CI lintet vendor/ und scripts/ nicht — der groesste Teil des als root laufenden Codes ist ungelintet — .forgejo/workflows/shellcheck.yml:24,33,37 — shellcheck/bash -n decken nur ct/*.sh install/*.sh misc/*.func ab. Von 17 Shell-Dateien werden 9 nicht geprueft: alle 7 unter vendor/tafforge-install/ (inkl. der komplexen, als root im LXC laufenden lxc/install.sh) plus scripts/dry-test/{run.sh,matrix.sh}. Ein Quoting-/set -e-Regressionsfehler beim naechsten vendor-Refresh wuerde von der CI nicht erkannt; der Drift-Probe-Step prueft nur env-Var-Namen, nicht die Shell-Korrektheit. Empfehlung: Lint-Targets um vendor/**/*.sh und scripts/**/*.sh erweitern (ggf. mit tolerantem Disable-Set, da vendor verbatim von upstream kommt — z.B. nur bash -n + ausgewaehlte SC-Codes).

  • [LOW] Kein post-install Diagnose-/Status-Tool (ct/doctor.sh) — Troubleshooting nur manuell — README.md:166-177, vendor/tafforge-install/lxc/install.sh (Health-Check nur bei Install), ct/update.sh:30 — Nach dem Install gibt es kein Wrapper-Tool, das den Zustand vom PVE-Host aus prueft; der Health-Check laeuft nur einmal bei Erstinstallation. ct/update.sh hat bereits die LXC-Erkennung (via /etc/tafforge/proxmox.meta), die fuer ein ct/doctor.sh wiederverwendbar waere. Empfehlung: Schlankes ct/doctor.sh (Spiegel zu update.sh): systemctl is-active, curl …/api/health, Plugin-Dir/mp0-Mount-Check, TC-Reachability — ein 1-Kommando-Gesundheitsbild ohne pct enter.

  • [LOW] Wrapper-Versionsanzeige fehlt im Header und im LXC-description-Block — ct/teddytafforge.sh:46, misc/build.func:103-114,642-652 — WRAPPER_VERSION (Default 0.1.0-dev) wird in /etc/tafforge/proxmox.meta geschrieben, aber dem Operator beim Lauf nie angezeigt (weder im ASCII-Header noch im description()-Block, der TAFFORGE_REF listet). Kein --version/-h-Handler im Host-Entrypoint. Bei curl|bash aus branch/main ist die laufende Version sonst nicht erkennbar — relevant fuer Bug-Reports. Empfehlung: WRAPPER_VERSION im Header-Banner und in description() ausgeben; optional --version/-h-Kurzhandler.

  • [LOW] ct/update.sh refresht die vendored install/-Schicht nicht — Drift zwischen App- und Wrapper-Stack — ct/update.sh:49, install/teddytafforge-install.sh:44-60, vendor/tafforge-install/lxc/update-app.sh:50-52 — ct/update.sh ruft nur den In-LXC-update-app.sh (App-Update). Die beim Erstinstall via curl gelegte vendored install/-Schicht (install/lib/*, install/lxc/*) wird beim Update nicht erneuert; verbessert der Wrapper z.B. install-plugin.sh, bekommt eine bestehende Instanz das nie. (Haengt eng mit dem detached-HEAD-No-Op aus Finding 3.1 zusammen.) Empfehlung: In ct/update.sh vor dem App-Update die vendored install/lib+install/lxc erneut aus REPO_RAW_BASE ziehen; update-app.sh ref-bewusst machen.

  • [LOW] TafForge-Port und onboot im Wrapper-Dialog nicht konfigurierbar — misc/build.func:284-365,641, vendor/tafforge-install/lxc/tafforge.service:13 — Der whiptail-Flow fragt CTID/Storage/CPU/RAM/Disk/Bridge/IP/MAC/VLAN/MTU/SSH/IPv6/Verbose ab, aber nicht den TafForge-Port (hart 3000 in description/Health/Service) oder onboot. Da TAFFORGE_PORT bereits sauber durch die Kette fliesst, waere ein optionaler Dialog billig — relevant bei mehreren Instanzen hinter einem Reverse-Proxy. Empfehlung: Optionalen TAFFORGE_PORT-Dialog ergaenzen, Wert via pct exec env durchreichen und die description()-URL aus dem Port bilden statt der Konstante 3000.

  • [LOW] teddycloud-install.sh ueberspringt network_check/setting_up_container/update_os — nur bei direktem Standalone-Aufruf relevant — install/teddycloud-install.sh:31,35, install/all-in-one-install.sh:19-21, misc/install.func (/run/tafforge-prep.done-Gate) — Im normalen all-in-one-Flow ist das korrekt (der Orchestrator macht die Prep einmal, das prep-Gate verhindert Doppel-apt). Aber teddycloud-install.sh ist via URL-Liveness-CI als eigenstaendig curl|bash-bar gefuehrt; ein Operator, der nur den TC-Installer direkt aufruft, bekommt keinen network_check → der erste apt-get update (:35) kann auf einem noch nicht geleasten DHCP-LXC fehlschlagen. Empfehlung: In teddycloud-install.sh nach verb defensiv network_check aufrufen (durch das prep-Gate im all-in-one-Flow ohnehin No-Op) oder klarstellen, dass der Installer nur ueber all-in-one laufen soll.

  • [LOW] Keine Release-Tags / kein WRAPPER_VERSION-Bump-Prozess trotz SemVer-Anspruch — README.md:240-247, CHANGELOG.md ([Unreleased]), ct/teddytafforge.sh:46 — README erklaert SemVer und proxmox.meta-Versionsanzeige; faktisch hat das Repo release_counter: 0 und WRAPPER_VERSION ist hart 0.1.0-dev. Fuer ein branch/main-curl|bash-Tool meldet damit jede Instanz 0.1.0-dev unabhaengig vom Stand. (Voraussetzung fuer den Tag-basierten Pin aus #2/#3.) Empfehlung: Leichten Release-Flow etablieren: Tag setzen, [Unreleased] in eine datierte Version ueberfuehren, WRAPPER_VERSION aus Tag/CHANGELOG ableiten.

6. Struktur, Ordnung, Dateileichen, Templates

  • [LOW] Veroeffentlichtes Repo ohne Issue-Forms/config.yaml und ohne committete Issue-/PR-Templates — git ls-tree -r origin/main .forgejo → nur workflows/{shellcheck,syntax-validate}.yml — Lokal liegen .forgejo/ISSUE_TEMPLATE/ai-task.md und .forgejo/PULL_REQUEST_TEMPLATE/ai-pr.md, sind aber via .git/info/exclude:11 untracked und damit nie committet. Die Org-Standard-Konvention (config.yaml mit blank_issues_enabled:false + YAML-Issue-Forms) fehlt auf main. Empfehlung: Org-config.yaml + die vorhandenen ai-task/ai-pr-Templates committen (aus .git/info/exclude nehmen) oder die Abweichung bewusst dokumentieren.

  • [LOW] Kein zentraler Org-Security-Workflow-Caller (@v2) — .forgejo/workflows/ — Die Workflows sind vollstaendig projekt-spezifisch (shellcheck/bashate/bash -n, Dryrun, README-Link-Check, Drift-Probe); kein Aufruf des org-weiten reusable Security-Workflows (uses: …/ci-workflows…@v2, kein gitleaks/secret-scan-Profil). Fuer ein reines Bash-Repo ohne Dependency-Manifest ist Trivy/SBOM begrenzt wertvoll, aber die fleet-weite Org-Konvention wird hier nicht eingehalten. Empfehlung: Entscheiden, ob ein thin Security-Caller (passendes Bash-Profil: Shellcheck/Hadolint/gitleaks) ergaenzt wird, oder die Abweichung bewusst dokumentieren.

  • [INFO] Fehlende ergaenzende Standarddateien (CONTRIBUTING.md, SECURITY.md, .editorconfig) — origin/main-Tree — Pflicht-Dateien vorhanden (README, LICENSE=MIT, .gitignore). Fuer ein oeffentliches Repo, dessen Kernfunktion curl … | bash als root ist, waere eine SECURITY.md mit Meldekanal + Verifikationshinweis angebracht. Reines Hygiene-Thema. Empfehlung: Knappe SECURITY.md (Meldekanal + curl|bash-Verifikationshinweis); optional .editorconfig (sh: 4 spaces/LF) und CONTRIBUTING/Verweis auf AGENTS.md.

  • [INFO] Als TEMP markierter DEBUG/xtrace-Block weiterhin im Produktiv-Pfad — misc/build.func:44-61, ct/teddytafforge.sh:23-29, CHANGELOG.md — Explizit markiert „TEMP: remove this section once the install flow is validated on real PVE". DEBUG-gated (Default 0), kein Laufzeit-Risiko im Normalbetrieb, aber als zu entfernender Uebergangscode gekennzeichnet (Follow-up zum geschlossenen Issue #1; kein offener Tracker dafuer). Empfehlung: Entscheidung treffen: nach erfolgter Real-PVE-Validierung entfernen ODER als dauerhaftes Diagnose-Feature entmarkieren und in README/CHANGELOG als stabil fuehren. (Siehe auch Security-Finding 4.5.)

Keine Dateileichen: keine *.bak/*.orig/*.rej/.DS_Store/Thumbs.db/*.log, keine committeten Binaries (groesste getrackte Datei misc/build.func = 28 KB), kein .github-Anti-Pattern (nur .forgejo/workflows). renovate.json existiert auf origin/main (Commit aad7f31), Renovate-Dashboard = Issue #8. Working-Tree clean.

7. Status vs. offene Issues & PRs

Offen real (Forgejo, verifiziert): Issues #3 (high, mutable Supply-Chain-Ref) + #8 (Renovate Dependency Dashboard, Bot-managed). PRs #5 (open) + #7 (open). open_issues_count=2, open_pr_counter=2.

  • PR #7 (fix/followup-2026-05-28, head 5799d17, base aad7f31) — pinnt TAFFORGE_REF_DEFAULT/TAFFORGE_REF auf v0.2.2, Closes #3, mergeable=true (kein Konflikt), seit 2026-05-29 unveraendert offen. Verifiziert NICHT auf main (git merge-base --is-ancestor 5799d17 origin/main => NEIN; origin/main:misc/build.func:24 zeigt …:-main). Einziger echter Action-Hebel — aber sollte erst nach dem Update-Pfad-Fix (Finding 3.1) gemergt werden, sonst toter Updater.
  • PR #5 (analysis/findings-2026-05-28, head 7f1db72) — reiner Analyse-/Tracking-PR ohne mergebaren Code-Diff, stale. Seine drei High-Punkte sind anderweitig abgehandelt: #4 (gefixt via PR #6, gemergt), #2 (geschlossen als Risikoakzeptanz), #3 (laeuft ueber PR #7). Empfehlung: schliessen (nicht mergen).
  • Issue #4 (Sidecar-Plugin-Clobber) — erledigt + auf origin/main verifiziert: vendor/tafforge-install/lxc/install.sh:230-244 captured _WRAPPER_TEDDYCLOUD_DATA_DIR/_WRAPPER_PLUGIN_ROOT vor dem Sourcen von tafforge.env und re-exportiert sie davor PLUGIN_ROOT_RESOLVED berechnet wird (:250). Kein Handlungsbedarf.
  • Issue #2 (curl|bash) — als Operator-Risikoakzeptanz geschlossen. Die im Schliessungs-Kommentar zugesagte README-Haertung ist teilweise vorhanden (Inspect-Pfad README.md:35-36 + trade-off :225), aber es fehlt eine formale Threat-Model-Sektion und jede Checksum/Pin-Massnahme. Restrisiko siehe Security 4.1.
  • Issue #1 (silent abort) — geschlossen + gefixt; der eingefuehrte TEMP-DEBUG-Block ist die offene Aufraeum-Schuld (kein Tracker).
  • description() zeigt dem Operator weiterhin main als TafForge-Ref (origin/main:misc/build.func:646 TafForge ref: ${TAFFORGE_REF_DEFAULT} mit …:-main). Wird mit Merge von PR #7 automatisch v0.2.2 — erledigt den Operator-Sichtbarkeits-Teil von #3.

Drift local↔remote: Lokaler Klon = PR-#7-Head (1 ahead / 1 behind origin/main); kennt den Renovate-Commit aad7f31/renovate.json nicht. Reiner Branch-/Fetch-Drift, kein Code-Konflikt (die divergierenden Dateien renovate.json vs. build.func-Pin ueberschneiden sich nicht → PR #7 mergeable=true). Wer den lokalen Klon naiv als Code-Stand liest, sieht faelschlich v0.2.2 (gepinnt); produktiv auf main ist es main (ungepinnt). Bei jeder Statusbewertung origin/main als Ground-Truth nehmen.

8. Priorisierte Empfehlungen

Prioritaet Thema Aufwand Bezug
P1 Update-Pfad ref-bewusst machen (update-app.sh: detached/Tag → fetch+checkout statt pull --ff-only), bevor ein Tag-Pin ausgeliefert wird M Finding 3.1 / #3 / PR #7
P1 PR #7 nach main mergen (App-Pin v0.2.2) — erst nach P1-Update-Fix — schliesst #3 + macht description()-Ref korrekt S #3 / PR #7
P1 Supply-Chain haerten: REPO_RAW_BASE auf unveraenderlichen Ref pinnen + sha256/Signatur-Verifikation der curl-Artefakte; Release-/Tag-Prozess (auch Voraussetzung fuer WRAPPER_VERSION) L #2 (Restrisiko), Sec 4.1, QOL 5.7
P2 IPv6 reparieren: disable_ipv6() verdrahten + DISABLE_IPV6 im pct exec env-Block durchreichen — ODER Funktion+Dialog entfernen S Findings 3.2 + 3.3
P2 teddycloud.service haerten (NoNewPrivileges/ProtectHome/PrivateTmp, ggf. User=+CAP_NET_BIND_SERVICE); README: TC-Admin ohne Auth M Sec 4.4
P2 README: TafForge bindet 0.0.0.0 ohne Wrapper-Auth → Reverse-Proxy/VPN-Hinweis; ALLOW_NON_YOUTUBE_SOURCES-Default ueberdenken S Sec 4.3
P2 CI-Lint auf vendor/**/*.sh + scripts/**/*.sh erweitern S QOL 5.1
P2 Sidecar-Update: mp0-Pfad als PLUGIN_ROOT/TEDDYCLOUD_DATA_DIR persistieren, damit Update-Plugin greift S Finding 3.4
P3 IP-Validatoren: 5.-Oktett abweisen XS Finding 3.5
P3 TEMP-DEBUG/xtrace entscheiden (entfernen oder entmarkieren + mktemp/0600-Log) S Sec 4.5, Struktur INFO
P3 ct/doctor.sh, WRAPPER_VERSION-Anzeige, Port-Dialog, vendored-install/-Refresh in ct/update.sh, teddycloud-install.sh network_check M QOL 5.2–5.6
P3 PR #5 schliessen (stale Tracking); Org-Templates (config.yaml/Issue-Forms), SECURITY.md committen S Status, Struktur 6

9. Methodik & Vertrauensgrad

  • Agenten: Recon + 5 Blickrichtungen (bugs, security, features_qol, structure, status) + 1 adversariale Verifikations-Stufe (dieser Bericht).
  • Direkt am Code verifiziert (origin/main + lokaler PR-Branch + Forgejo-API): der Pin-Drift (git merge-base, git show origin/main:), disable_ipv6 tot (grep zeigt nur Definition), DISABLE_IPV6 nicht im pct exec env-Block, IP-Validator-Bug (empirisch reproduziert: 1.2.3.4.5 akzeptiert), 0.0.0.0-Bind + Hardening-Gefaelle TafForge↔TeddyCloud (Units gelesen), curl|bash-Kette, TEMP-Block, CI-Lint-Coverage, Issue #4-Fix auf main, Forgejo-Issue/PR-Stati (#1–#8), keine committeten Secrets.
  • Korrekturen an Roh-Findings: (a) Ein Status-Agent behauptete, das README habe keinen Inspect/Verify-Pfad — falsch: README.md:35-36 („Paranoid alternative", download→less→run) und :225 (trade-off) existieren; es fehlt nur Checksum/Signatur/Pin und eine formale Threat-Model-Sektion. (b) Recon-Annahmen „Pin v0.2.2 ist Code-Stand" / „Renovate fehlt" wurden verworfen — Pin nur in PR #7, Renovate auf main vorhanden. (c) Der TEMP-Block und die Supply-Chain-/mutable-Ref-Punkte tauchten mehrfach auf und wurden je einmal konsolidiert.
  • Nicht pruefbar (klar markiert): Die App-interne AuthN/AuthZ-Lage von TafForge (App-Code liegt im externen GitHub-Mirror, nicht in diesem Repo). Reales Laufzeitverhalten auf einem echten PVE-Host (pct create, In-LXC-Installer-Kette, mp0-Bind, whiptail-TTY-Pfade) — nur via DRY_RUN-Matrix abgedeckt, nicht real ausgefuehrt (OAM-Schutz; keine Builds/Installs/Deploys durchgefuehrt).
  • Severity-Verteilung (nur confirmed+uncertain): 0 critical, 2 high, 4 medium, 11 low, 2 info. Alle aufgenommenen Findings sind confirmed; keine uncertain verbleibend (zwei Halluzinationen/Fehlannahmen verworfen).
# Deep Analysis 2026-06-04 — teddytafforge-proxmox _Autonome Multi-Agent-Analyse (Recon + 5 Blickrichtungen + adversariale Verifikation). Stand 2026-06-04._ ## 1. Ueberblick & Einordnung - **repo_class:** `full` — reiner Bash/POSIX-sh Installations-Wrapper, **kein Anwendungscode**. - **Zweck:** Ein-Kommando-Deploy von TafForge (Custom-Tonie-`.taf`-Builder) und optional TeddyCloud als **unprivilegierter Debian-13-LXC** auf Proxmox VE. Topologien: `sidecar` (TC extern, mp0-Bind-Mount) und `all-in-one` (TC nativ im selben LXC gebaut). - **Stack:** Host-Entrypoint `ct/teddytafforge.sh` → `misc/build.func` (+`install.func`) → In-LXC-Installer `install/*.sh` → verbatim vendored Upstream-Installer `vendor/tafforge-install/`. systemd-Units, whiptail-Dialoge, DRY_RUN-Matrix-Dry-Test, 3-stufige Forgejo-CI (shellcheck/bashate, `bash -n`, Drift-Probe + URL-Liveness). - Code-Qualitaet der Shell-Logik ueberdurchschnittlich: `pct`-Argumente via Bash-Arrays (keine Command-Injection), Eingabe-Validatoren, ERR-Traps mit Stack-Trace, Rollback-Traps fuer halb-gebaute LXCs, gehaertete TafForge-systemd-Unit. **Wichtige Drift-Korrektur (alle 5 Agenten bestaetigt + adversarial verifiziert):** Der lokale Klon steht auf Branch `fix/followup-2026-05-28` (HEAD `5799d17` = **offener PR #7**), **nicht** auf `main` (`aad7f31`). `git merge-base --is-ancestor 5799d17 origin/main` => **NEIN**. Der Versions-Pin `v0.2.2` existiert nur im PR-Branch; **`origin/main` traegt weiterhin den mutable `main`-Ref** (verifiziert via `git show origin/main:`). Wer das public Repo per `curl|bash` nutzt, zieht den ungepinnten Stand. Befunde sind daher gegen **`origin/main` als Ground-Truth** formuliert. ## 2. Gesamtbewertung **Health-Grade: B** Strukturell sauberes, sorgfaeltig gebautes Wrapper-Repo mit guter Defensive (Array-basierte `pct`-Aufrufe, Traps, Validatoren, gehaertete TafForge-Unit, Matrix-Dry-Test, mehrstufige CI). **Keine committeten Secrets** (`.claude/settings.local.json` = 0 Bytes; `.gitignore` deckt `.env`/`*.key`/`*.pem`). Die offenen Punkte sind real, aber begrenzt: das dominierende Thema ist das **Supply-Chain-/Ausfuehrungsmodell** (`curl|bash` als root ohne Integritaetspruefung + mutable App-Ref auf `main`), dazu zwei vollstaendig **tote IPv6-Pfade** und eine **Update-Pfad-Regression**, die der geplante Tag-Pin (PR #7) *einfuehrt*. Kein einzelnes Critical. B statt A wegen der ungeloesten Supply-Chain-Lage und der Tatsache, dass der einzige fertige Fix (PR #7) seit 2026-05-29 ungemergt haengt — und so wie er ist den Updater bricht. ## 3. Bugs & Korrektheit - **[MEDIUM]** `git pull --ff-only` wird nach Tag-Klon zum stillen No-Op — Update-Pfad bricht durch den v0.2.2-Pin — `vendor/tafforge-install/lxc/update-app.sh:50-52`, `install/teddytafforge-install.sh:41,83-86` — Der In-LXC-Installer klont die App mit `git clone --depth=1 --branch "${TAFFORGE_REF}"` und kopiert `.git` nach `${APP_DIR}/.git`, damit `update-app.sh` den git-Pfad nimmt. Sobald `TAFFORGE_REF` ein **Tag** ist (PR #7: `v0.2.2`), erzeugt der Tag-Klon einen **detached HEAD** ohne Tracking-Branch; `git pull --ff-only` meldet dann immer „Bereits aktuell" bzw. schlaegt fehl — der App-Code bleibt fuer immer auf `v0.2.2`, nur pip/npm laufen. **Auf `main` (Branch-Klon) funktioniert der Updater; der Tag-Pin aus PR #7 fuehrt die Regression erst ein.** *Empfehlung:* `update-app.sh` ref-bewusst machen — bei detached/Tag-Checkout `git fetch origin "$APP_REPO_REF" && git checkout "$APP_REPO_REF"` statt blindem `pull --ff-only`; mindestens detached HEAD erkennen und ehrlich melden statt stillem No-Op. (Voraussetzung bevor PR #7 / Tag-Pins ausgeliefert werden; bezieht sich auf #3 / PR #7.) - **[MEDIUM]** `disable_ipv6()` ist toter Code — wird nie aufgerufen — `misc/install.func:47-54` — Die Funktion schreibt per sysctl `/etc/sysctl.d/99-disable-ipv6.conf`, wenn `DISABLE_IPV6=yes`, wird aber in **keinem** Skript der Kette aufgerufen (`grep -rn 'disable_ipv6'` zeigt nur die Definition). Die whiptail-Frage „Disable IPv6 in the LXC? (Recommended on networks without IPv6 to avoid apt hangs.)" (`build.func:354`) suggeriert eine In-LXC-IPv6-Abschaltung, die faktisch nie passiert — `DISABLE_IPV6` wirkt nur auf `build.func:525` (`,ip6=auto` im `net_arg` weglassen), was den IPv6-Stack im LXC nicht deaktiviert; der versprochene apt-Hang-Schutz greift nicht. *Empfehlung:* `disable_ipv6` in `setting_up_container()` (oder am Anfang der In-LXC-Installer) aufrufen ODER Funktion + irrefuehrende Dialogzusage entfernen. - **[LOW]** `DISABLE_IPV6` wird nicht an den In-LXC-Installer durchgereicht — `misc/build.func:605-616`, `misc/install.func:48` — Selbst wenn `disable_ipv6()` verdrahtet waere, koennte es nie greifen: der `pct exec ... -- env`-Block (605-616) listet `DISABLE_IPV6` **nicht** auf. Die In-LXC-Skripte sehen die Variable nie; `${DISABLE_IPV6:-no}` wertet immer zu `no`. Zwei unabhaengige Defekte (nie aufgerufen + nie uebergeben) sorgen gemeinsam dafuer, dass die In-LXC-Abschaltung unter keinen Umstaenden laeuft. *Empfehlung:* `DISABLE_IPV6="$DISABLE_IPV6"` in den `pct exec env`-Block aufnehmen (zusammen mit dem Aufruf aus dem vorigen Finding). - **[LOW]** Sidecar-Update re-templatet das Plugin auf den all-in-one-Default-Pfad — `vendor/tafforge-install/lxc/update-app.sh:87`, `tafforge.env.sample:17,20`, `misc/build.func:417` — `update-app.sh` ruft `install-plugin.sh` mit `"${PLUGIN_ROOT:-/opt/teddycloud/data/www/plugins}"` (all-in-one-Layout). Im Sidecar liegt das Ziel unter dem mp0-Mount (Default `/mnt/teddycloud-data/.../plugins`). `PLUGIN_ROOT` ist in `tafforge.env.sample` auskommentiert, `TEDDYCLOUD_DATA_DIR=/opt/teddycloud/data` fest gesetzt; beim Sidecar-Update wird `PLUGIN_ROOT` nicht gesetzt → Default greift → `install-plugin.sh` laeuft gegen einen im Sidecar nicht existierenden Pfad und ueberspringt (per `|| true`, kein Abbruch) die Aktualisierung. **Erstinstallation ist korrekt** (Wrapper uebergibt dort den mp0-Pfad); nur der spaetere Update-Lauf verliert diesen Kontext. *Empfehlung:* Bei Wrapper-Installs den real gewaehlten mp0-Pfad als `PLUGIN_ROOT`/`TEDDYCLOUD_DATA_DIR` in `/etc/tafforge/tafforge.env` persistieren, oder in `update-app.sh` aus `/etc/tafforge/proxmox.meta` ableiten. - **[LOW]** `valid_ipv4`/`valid_cidr` akzeptieren 5+ Oktette (z.B. `1.2.3.4.5`) — `misc/build.func:216,223` — Beide nutzen `IFS=. read -r a b c d _ <<< "$addr"` und pruefen nur `[ -n "$d" ]`. Ein fuenftes Feld landet in `_` und wird nicht zurueckgewiesen. **Empirisch reproduziert:** `valid_ipv4 '1.2.3.4.5'` => return 0, `valid_cidr '1.2.3.4.5/24'` => return 0; zu kurze Adressen (`1.2.3`) werden korrekt abgewiesen, zu lange nicht. Geringe Auswirkung (Wert geht in `pct ... ip=`, das selbst validiert), aber die Validierungsfunktion erfuellt ihren Zweck nicht vollstaendig. *Empfehlung:* Trailing-Feld pruefen: `IFS=. read -r a b c d e <<< "$addr"; [ -n "$d" ] && [ -z "$e" ] || return 1`. ## 4. Security > Oeffentliches Repo — Details verallgemeinert, keine Exploit-Schritte. - **[HIGH]** Mehrstufiges `curl|bash`/`source <(curl …)` als root ohne Integritaetspruefung (keine Checksum/Signatur/Commit-Pin) — `ct/teddytafforge.sh:15,19`, `misc/build.func:605-616`, `install/all-in-one-install.sh:14,37,41`, `install/teddycloud-install.sh` — Jede Installer-Ebene bootstrappt fremden Code ohne GPG-Signatur, sha256-Vergleich oder Commit-Pinning. `REPO_RAW_BASE` zeigt hartkodiert auf `/raw/branch/main` (beweglicher Branch). `build.func` laeuft nach `root_check` als root und fuehrt via `pct exec ... bash -c "curl … | bash"` weiteren Code in der LXC aus; TLS schuetzt nur den Transport, nicht die Integritaet des Inhalts. Eine Kompromittierung von Forgejo/GitHub, ein boesartiger Commit/PR-Merge oder eine TLS-Interception (rogue CA) ergibt root-Codeausfuehrung auf dem PVE-Host. **Status:** als Forgejo-Issue **#2** getrackt, am 2026-05-29 als bewusste **Operator-Risikoakzeptanz geschlossen** (Repo hat keine Tags / keine Release-Pipeline). **Teil-Minderung vorhanden:** README enthaelt eine „Paranoid alternative" (download → `less` → run, `README.md:35-36`) und einen „security trade-off"-Hinweis (`README.md:225`) — entgegen einem Agenten-Befund existiert ein Inspect-Pfad. Es fehlt aber weiterhin jede **Pin-/Checksum-/Signatur-Massnahme**. *Empfehlung:* `REPO_RAW_BASE` auf einen unveraenderlichen Ref (Tag/Commit-SHA) pinnen und per-curl-Artefakte gegen ein eingechecktes sha256-Manifest/Signatur verifizieren; Release-/Tag-Prozess fuer das Wrapper-Repo etablieren. (Bereits getrackt als #2 — Restrisiko bleibt; nicht als neu darstellen.) - **[HIGH]** Default-Branch `main` klont die TafForge-App weiterhin vom beweglichen `main`-Ref (mutable Supply-Chain-Ref) — `misc/build.func:24` (`origin/main`), `install/teddytafforge-install.sh:21` (`origin/main`), `vendor/tafforge-install/lxc/install.sh` (`git reset --hard origin/$APP_REPO_REF`), `update-app.sh:52` — Die App laeuft als (Service-User-)uvicorn-systemd-Dienst und wird per `git clone --branch "${TAFFORGE_REF}"` geklont. **Auf `origin/main` verifiziert:** `TAFFORGE_REF_DEFAULT="${TAFFORGE_REF_DEFAULT:-main}"` (build.func:24) und `TAFFORGE_REF="${TAFFORGE_REF:-main}"` (teddytafforge-install.sh:21). Der Pin `v0.2.2` existiert nur im offenen **PR #7** (`Closes #3`, `mergeable=true`), ist also nicht ausgeliefert. **TeddyCloud ist dagegen auf Tag `tc_v0.6.8` gepinnt** (build.func:26) — die Inkonsistenz unterstreicht die fehlende App-Pinnung. *Empfehlung:* PR #7 nach `main` mergen — aber **erst nach** dem Update-Pfad-Fix (Finding 3.1), sonst tauscht man den mutable Ref gegen einen toten Updater; zusaetzlich den geklonten Tree gegen einen erwarteten Commit-SHA verifizieren. (Bezieht sich auf Issue **#3** / PR **#7**.) - **[MEDIUM]** TafForge-Web-UI bindet auf `0.0.0.0:${TAFFORGE_PORT}` (Default 3000) ohne vom Wrapper eingerichtete Authentifizierung — `vendor/tafforge-install/lxc/tafforge.service:13`, `vendor/tafforge-install/lib/env-defaults.sh:19,31,37`, `misc/build.func:641` — Die systemd-Unit startet uvicorn mit `--host 0.0.0.0`; der Wrapper richtet keinerlei Auth, Reverse-Proxy-Auth oder Firewall ein und bewirbt die UI offen unter `http://<lxc-ip>:3000/`. TafForge bietet Datei-Upload (`MAX_UPLOAD_BYTES=2 GiB`) und yt-dlp-Outbound-Fetch (`ALLOW_NON_YOUTUBE_SOURCES=true` per Default) — d.h. ein per Default LAN-weit erreichbarer Dienst mit Upload-/Fetch-Oberflaeche. **Ob die App selbst AuthN/AuthZ hat, ist hier nicht pruefbar** (App-Code liegt extern im GitHub-Mirror). *Empfehlung:* Im README klar dokumentieren, dass der Dienst ohne Auth LAN-weit erreichbar ist und hinter authentifizierenden Reverse-Proxy/VPN gehoert; optional `127.0.0.1`-Bind bzw. konfigurierbares Bind-Interface als sicherere Default-Variante und `ALLOW_NON_YOUTUBE_SOURCES`-Default ueberdenken. - **[MEDIUM]** TeddyCloud-Dienst laeuft als root ohne systemd-Hardening; Hardening-Gefaelle gegenueber der TafForge-Unit — `install/teddycloud-install.sh:120-136`, `:73`, `:143` — Im all-in-one-Pfad wird TeddyCloud nativ als root gebaut (`make … zip`) und die erzeugte `teddycloud.service` laeuft **ohne `User=`/`Group=` und ohne jegliche `Protect*`/`NoNewPrivileges`-Direktiven** (= root), waehrend `tafforge.service` vorbildlich gehaertet ist (`User=tafforge`, `NoNewPrivileges`, `ProtectSystem=strict`, `ProtectHome`, `PrivateTmp` …). TC bindet Admin-Ports 80/443/8443 ohne Wrapper-seitiges Auth/Firewalling. **Mitigiert** durch den unprivilegierten LXC (`build.func` `--unprivileged 1`) — ein root-Kompromiss im Container ist nicht automatisch root auf dem Host. *Empfehlung:* `teddycloud.service` mindestens mit `NoNewPrivileges=true`, `ProtectHome`, `PrivateTmp` versehen; falls TC einen Service-User toleriert, `User=`+`ReadWritePaths` setzen und `AmbientCapabilities=CAP_NET_BIND_SERVICE` statt vollem root fuer Ports <1024. Im README dokumentieren, dass TC-Admin ohne Auth erreichbar ist. - **[LOW]** TEMP-Debug/xtrace-Block schreibt Trace nach vorhersehbarem `/tmp`-Pfad (kein `mktemp`) — `misc/build.func:48-53,603`, `ct/teddytafforge.sh:23-29` — Mit `DEBUG=1` wird ein voller `bash -x`-Trace nach `/tmp/teddytafforge-trace-$$.log` geschrieben; `build.func:603` legt zusaetzlich `/tmp/tafforge-install-${CTID}.log` ohne explizite Permissions/`mktemp` an. Pfade sind vorhersehbar (PID/CTID), was auf einem Mehrbenutzer-PVE-Host theoretisch Symlink-Risiken erlaubt; praktisch laeuft der Host-Teil nur als root und der Trace nutzt einen dedizierten FD 19. Je nach ENV-Werten koennen Betriebsdaten in den `/tmp`-Log gelangen. *Empfehlung:* Block vor breiter Auslieferung entfernen (von den Autoren selbst als TEMP markiert) oder hinter explizites Opt-in mit `install -m 0600`/`mktemp`-Log absichern. ## 5. Features & QOL - **[MEDIUM]** CI lintet `vendor/` und `scripts/` nicht — der groesste Teil des als root laufenden Codes ist ungelintet — `.forgejo/workflows/shellcheck.yml:24,33,37` — `shellcheck`/`bash -n` decken nur `ct/*.sh install/*.sh misc/*.func` ab. Von 17 Shell-Dateien werden 9 **nicht** geprueft: alle 7 unter `vendor/tafforge-install/` (inkl. der komplexen, als root im LXC laufenden `lxc/install.sh`) plus `scripts/dry-test/{run.sh,matrix.sh}`. Ein Quoting-/`set -e`-Regressionsfehler beim naechsten vendor-Refresh wuerde von der CI nicht erkannt; der Drift-Probe-Step prueft nur env-Var-Namen, nicht die Shell-Korrektheit. *Empfehlung:* Lint-Targets um `vendor/**/*.sh` und `scripts/**/*.sh` erweitern (ggf. mit tolerantem Disable-Set, da vendor verbatim von upstream kommt — z.B. nur `bash -n` + ausgewaehlte SC-Codes). - **[LOW]** Kein post-install Diagnose-/Status-Tool (`ct/doctor.sh`) — Troubleshooting nur manuell — `README.md:166-177`, `vendor/tafforge-install/lxc/install.sh` (Health-Check nur bei Install), `ct/update.sh:30` — Nach dem Install gibt es kein Wrapper-Tool, das den Zustand vom PVE-Host aus prueft; der Health-Check laeuft nur einmal bei Erstinstallation. `ct/update.sh` hat bereits die LXC-Erkennung (via `/etc/tafforge/proxmox.meta`), die fuer ein `ct/doctor.sh` wiederverwendbar waere. *Empfehlung:* Schlankes `ct/doctor.sh` (Spiegel zu `update.sh`): `systemctl is-active`, `curl …/api/health`, Plugin-Dir/mp0-Mount-Check, TC-Reachability — ein 1-Kommando-Gesundheitsbild ohne `pct enter`. - **[LOW]** Wrapper-Versionsanzeige fehlt im Header und im LXC-`description`-Block — `ct/teddytafforge.sh:46`, `misc/build.func:103-114,642-652` — `WRAPPER_VERSION` (Default `0.1.0-dev`) wird in `/etc/tafforge/proxmox.meta` geschrieben, aber dem Operator beim Lauf nie angezeigt (weder im ASCII-Header noch im `description()`-Block, der `TAFFORGE_REF` listet). Kein `--version`/`-h`-Handler im Host-Entrypoint. Bei `curl|bash` aus `branch/main` ist die laufende Version sonst nicht erkennbar — relevant fuer Bug-Reports. *Empfehlung:* `WRAPPER_VERSION` im Header-Banner und in `description()` ausgeben; optional `--version`/`-h`-Kurzhandler. - **[LOW]** `ct/update.sh` refresht die vendored `install/`-Schicht nicht — Drift zwischen App- und Wrapper-Stack — `ct/update.sh:49`, `install/teddytafforge-install.sh:44-60`, `vendor/tafforge-install/lxc/update-app.sh:50-52` — `ct/update.sh` ruft nur den In-LXC-`update-app.sh` (App-Update). Die beim Erstinstall via curl gelegte vendored `install/`-Schicht (`install/lib/*`, `install/lxc/*`) wird beim Update **nicht** erneuert; verbessert der Wrapper z.B. `install-plugin.sh`, bekommt eine bestehende Instanz das nie. (Haengt eng mit dem detached-HEAD-No-Op aus Finding 3.1 zusammen.) *Empfehlung:* In `ct/update.sh` vor dem App-Update die vendored `install/lib`+`install/lxc` erneut aus `REPO_RAW_BASE` ziehen; `update-app.sh` ref-bewusst machen. - **[LOW]** TafForge-Port und `onboot` im Wrapper-Dialog nicht konfigurierbar — `misc/build.func:284-365,641`, `vendor/tafforge-install/lxc/tafforge.service:13` — Der whiptail-Flow fragt CTID/Storage/CPU/RAM/Disk/Bridge/IP/MAC/VLAN/MTU/SSH/IPv6/Verbose ab, aber nicht den TafForge-Port (hart `3000` in `description`/Health/Service) oder `onboot`. Da `TAFFORGE_PORT` bereits sauber durch die Kette fliesst, waere ein optionaler Dialog billig — relevant bei mehreren Instanzen hinter einem Reverse-Proxy. *Empfehlung:* Optionalen `TAFFORGE_PORT`-Dialog ergaenzen, Wert via `pct exec env` durchreichen und die `description()`-URL aus dem Port bilden statt der Konstante 3000. - **[LOW]** `teddycloud-install.sh` ueberspringt `network_check`/`setting_up_container`/`update_os` — nur bei direktem Standalone-Aufruf relevant — `install/teddycloud-install.sh:31,35`, `install/all-in-one-install.sh:19-21`, `misc/install.func` (`/run/tafforge-prep.done`-Gate) — Im normalen all-in-one-Flow ist das korrekt (der Orchestrator macht die Prep einmal, das prep-Gate verhindert Doppel-apt). Aber `teddycloud-install.sh` ist via URL-Liveness-CI als eigenstaendig `curl|bash`-bar gefuehrt; ein Operator, der nur den TC-Installer direkt aufruft, bekommt keinen `network_check` → der erste `apt-get update` (`:35`) kann auf einem noch nicht geleasten DHCP-LXC fehlschlagen. *Empfehlung:* In `teddycloud-install.sh` nach `verb` defensiv `network_check` aufrufen (durch das prep-Gate im all-in-one-Flow ohnehin No-Op) oder klarstellen, dass der Installer nur ueber all-in-one laufen soll. - **[LOW]** Keine Release-Tags / kein `WRAPPER_VERSION`-Bump-Prozess trotz SemVer-Anspruch — `README.md:240-247`, `CHANGELOG.md` (`[Unreleased]`), `ct/teddytafforge.sh:46` — README erklaert SemVer und proxmox.meta-Versionsanzeige; faktisch hat das Repo `release_counter: 0` und `WRAPPER_VERSION` ist hart `0.1.0-dev`. Fuer ein `branch/main`-`curl|bash`-Tool meldet damit jede Instanz `0.1.0-dev` unabhaengig vom Stand. (Voraussetzung fuer den Tag-basierten Pin aus #2/#3.) *Empfehlung:* Leichten Release-Flow etablieren: Tag setzen, `[Unreleased]` in eine datierte Version ueberfuehren, `WRAPPER_VERSION` aus Tag/CHANGELOG ableiten. ## 6. Struktur, Ordnung, Dateileichen, Templates - **[LOW]** Veroeffentlichtes Repo ohne Issue-Forms/`config.yaml` und ohne committete Issue-/PR-Templates — `git ls-tree -r origin/main .forgejo` → nur `workflows/{shellcheck,syntax-validate}.yml` — Lokal liegen `.forgejo/ISSUE_TEMPLATE/ai-task.md` und `.forgejo/PULL_REQUEST_TEMPLATE/ai-pr.md`, sind aber via `.git/info/exclude:11` **untracked** und damit nie committet. Die Org-Standard-Konvention (`config.yaml` mit `blank_issues_enabled:false` + YAML-Issue-Forms) fehlt auf `main`. *Empfehlung:* Org-`config.yaml` + die vorhandenen ai-task/ai-pr-Templates committen (aus `.git/info/exclude` nehmen) oder die Abweichung bewusst dokumentieren. - **[LOW]** Kein zentraler Org-Security-Workflow-Caller (`@v2`) — `.forgejo/workflows/` — Die Workflows sind vollstaendig projekt-spezifisch (shellcheck/bashate/`bash -n`, Dryrun, README-Link-Check, Drift-Probe); kein Aufruf des org-weiten reusable Security-Workflows (`uses: …/ci-workflows…@v2`, kein gitleaks/secret-scan-Profil). Fuer ein reines Bash-Repo ohne Dependency-Manifest ist Trivy/SBOM begrenzt wertvoll, aber die fleet-weite Org-Konvention wird hier nicht eingehalten. *Empfehlung:* Entscheiden, ob ein thin Security-Caller (passendes Bash-Profil: Shellcheck/Hadolint/gitleaks) ergaenzt wird, oder die Abweichung bewusst dokumentieren. - **[INFO]** Fehlende ergaenzende Standarddateien (`CONTRIBUTING.md`, `SECURITY.md`, `.editorconfig`) — `origin/main`-Tree — Pflicht-Dateien vorhanden (README, LICENSE=MIT, `.gitignore`). Fuer ein **oeffentliches** Repo, dessen Kernfunktion `curl … | bash` als root ist, waere eine `SECURITY.md` mit Meldekanal + Verifikationshinweis angebracht. Reines Hygiene-Thema. *Empfehlung:* Knappe `SECURITY.md` (Meldekanal + curl|bash-Verifikationshinweis); optional `.editorconfig` (sh: 4 spaces/LF) und `CONTRIBUTING`/Verweis auf `AGENTS.md`. - **[INFO]** Als TEMP markierter DEBUG/xtrace-Block weiterhin im Produktiv-Pfad — `misc/build.func:44-61`, `ct/teddytafforge.sh:23-29`, `CHANGELOG.md` — Explizit markiert „TEMP: remove this section once the install flow is validated on real PVE". DEBUG-gated (Default 0), kein Laufzeit-Risiko im Normalbetrieb, aber als zu entfernender Uebergangscode gekennzeichnet (Follow-up zum geschlossenen Issue #1; kein offener Tracker dafuer). *Empfehlung:* Entscheidung treffen: nach erfolgter Real-PVE-Validierung entfernen ODER als dauerhaftes Diagnose-Feature entmarkieren und in README/CHANGELOG als stabil fuehren. (Siehe auch Security-Finding 4.5.) > **Keine Dateileichen:** keine `*.bak/*.orig/*.rej/.DS_Store/Thumbs.db/*.log`, keine committeten Binaries (groesste getrackte Datei `misc/build.func` = 28 KB), kein `.github`-Anti-Pattern (nur `.forgejo/workflows`). `renovate.json` existiert auf `origin/main` (Commit `aad7f31`), Renovate-Dashboard = Issue #8. Working-Tree clean. ## 7. Status vs. offene Issues & PRs **Offen real (Forgejo, verifiziert):** Issues **#3** (high, mutable Supply-Chain-Ref) + **#8** (Renovate Dependency Dashboard, Bot-managed). PRs **#5** (open) + **#7** (open). `open_issues_count=2`, `open_pr_counter=2`. - **PR #7** (`fix/followup-2026-05-28`, head `5799d17`, base `aad7f31`) — pinnt `TAFFORGE_REF_DEFAULT`/`TAFFORGE_REF` auf `v0.2.2`, `Closes #3`, **`mergeable=true`** (kein Konflikt), seit 2026-05-29 unveraendert offen. **Verifiziert NICHT auf main** (`git merge-base --is-ancestor 5799d17 origin/main` => NEIN; `origin/main:misc/build.func:24` zeigt `…:-main`). **Einziger echter Action-Hebel** — aber sollte erst **nach** dem Update-Pfad-Fix (Finding 3.1) gemergt werden, sonst toter Updater. - **PR #5** (`analysis/findings-2026-05-28`, head `7f1db72`) — reiner **Analyse-/Tracking-PR ohne mergebaren Code-Diff**, stale. Seine drei High-Punkte sind anderweitig abgehandelt: #4 (gefixt via PR #6, gemergt), #2 (geschlossen als Risikoakzeptanz), #3 (laeuft ueber PR #7). *Empfehlung:* schliessen (nicht mergen). - **Issue #4** (Sidecar-Plugin-Clobber) — **erledigt + auf `origin/main` verifiziert**: `vendor/tafforge-install/lxc/install.sh:230-244` captured `_WRAPPER_TEDDYCLOUD_DATA_DIR`/`_WRAPPER_PLUGIN_ROOT` vor dem Sourcen von `tafforge.env` und re-exportiert sie davor `PLUGIN_ROOT_RESOLVED` berechnet wird (`:250`). Kein Handlungsbedarf. - **Issue #2** (curl|bash) — als **Operator-Risikoakzeptanz geschlossen**. Die im Schliessungs-Kommentar zugesagte README-Haertung ist **teilweise** vorhanden (Inspect-Pfad `README.md:35-36` + trade-off `:225`), aber es fehlt eine formale Threat-Model-Sektion und jede Checksum/Pin-Massnahme. Restrisiko siehe Security 4.1. - **Issue #1** (silent abort) — geschlossen + gefixt; der eingefuehrte TEMP-DEBUG-Block ist die offene Aufraeum-Schuld (kein Tracker). - **`description()` zeigt dem Operator weiterhin `main`** als TafForge-Ref (`origin/main:misc/build.func:646` `TafForge ref: ${TAFFORGE_REF_DEFAULT}` mit `…:-main`). Wird mit Merge von PR #7 automatisch `v0.2.2` — erledigt den Operator-Sichtbarkeits-Teil von #3. **Drift local↔remote:** Lokaler Klon = PR-#7-Head (1 ahead / 1 behind `origin/main`); kennt den Renovate-Commit `aad7f31`/`renovate.json` nicht. **Reiner Branch-/Fetch-Drift, kein Code-Konflikt** (die divergierenden Dateien `renovate.json` vs. build.func-Pin ueberschneiden sich nicht → PR #7 `mergeable=true`). Wer den lokalen Klon naiv als Code-Stand liest, sieht faelschlich `v0.2.2` (gepinnt); produktiv auf `main` ist es `main` (ungepinnt). **Bei jeder Statusbewertung `origin/main` als Ground-Truth nehmen.** ## 8. Priorisierte Empfehlungen | Prioritaet | Thema | Aufwand | Bezug | |---|---|---|---| | **P1** | Update-Pfad ref-bewusst machen (`update-app.sh`: detached/Tag → `fetch`+`checkout` statt `pull --ff-only`), **bevor** ein Tag-Pin ausgeliefert wird | M | Finding 3.1 / #3 / PR #7 | | **P1** | PR #7 nach main mergen (App-Pin `v0.2.2`) — **erst nach P1-Update-Fix** — schliesst #3 + macht `description()`-Ref korrekt | S | #3 / PR #7 | | **P1** | Supply-Chain haerten: `REPO_RAW_BASE` auf unveraenderlichen Ref pinnen + sha256/Signatur-Verifikation der curl-Artefakte; Release-/Tag-Prozess (auch Voraussetzung fuer WRAPPER_VERSION) | L | #2 (Restrisiko), Sec 4.1, QOL 5.7 | | **P2** | IPv6 reparieren: `disable_ipv6()` verdrahten + `DISABLE_IPV6` im `pct exec env`-Block durchreichen — ODER Funktion+Dialog entfernen | S | Findings 3.2 + 3.3 | | **P2** | `teddycloud.service` haerten (`NoNewPrivileges`/`ProtectHome`/`PrivateTmp`, ggf. `User=`+`CAP_NET_BIND_SERVICE`); README: TC-Admin ohne Auth | M | Sec 4.4 | | **P2** | README: TafForge bindet `0.0.0.0` ohne Wrapper-Auth → Reverse-Proxy/VPN-Hinweis; `ALLOW_NON_YOUTUBE_SOURCES`-Default ueberdenken | S | Sec 4.3 | | **P2** | CI-Lint auf `vendor/**/*.sh` + `scripts/**/*.sh` erweitern | S | QOL 5.1 | | **P2** | Sidecar-Update: mp0-Pfad als `PLUGIN_ROOT`/`TEDDYCLOUD_DATA_DIR` persistieren, damit Update-Plugin greift | S | Finding 3.4 | | **P3** | IP-Validatoren: 5.-Oktett abweisen | XS | Finding 3.5 | | **P3** | TEMP-DEBUG/xtrace entscheiden (entfernen oder entmarkieren + `mktemp`/`0600`-Log) | S | Sec 4.5, Struktur INFO | | **P3** | `ct/doctor.sh`, WRAPPER_VERSION-Anzeige, Port-Dialog, vendored-`install/`-Refresh in `ct/update.sh`, `teddycloud-install.sh` `network_check` | M | QOL 5.2–5.6 | | **P3** | PR #5 schliessen (stale Tracking); Org-Templates (`config.yaml`/Issue-Forms), `SECURITY.md` committen | S | Status, Struktur 6 | ## 9. Methodik & Vertrauensgrad - **Agenten:** Recon + 5 Blickrichtungen (bugs, security, features_qol, structure, status) + 1 adversariale Verifikations-Stufe (dieser Bericht). - **Direkt am Code verifiziert (`origin/main` + lokaler PR-Branch + Forgejo-API):** der Pin-Drift (`git merge-base`, `git show origin/main:`), `disable_ipv6` tot (`grep` zeigt nur Definition), `DISABLE_IPV6` nicht im `pct exec env`-Block, IP-Validator-Bug (**empirisch reproduziert**: `1.2.3.4.5` akzeptiert), `0.0.0.0`-Bind + Hardening-Gefaelle TafForge↔TeddyCloud (Units gelesen), `curl|bash`-Kette, TEMP-Block, CI-Lint-Coverage, Issue #4-Fix auf main, Forgejo-Issue/PR-Stati (#1–#8), keine committeten Secrets. - **Korrekturen an Roh-Findings:** (a) Ein Status-Agent behauptete, das README habe keinen Inspect/Verify-Pfad — **falsch**: `README.md:35-36` („Paranoid alternative", download→`less`→run) und `:225` (trade-off) existieren; es fehlt nur Checksum/Signatur/Pin und eine formale Threat-Model-Sektion. (b) Recon-Annahmen „Pin v0.2.2 ist Code-Stand" / „Renovate fehlt" wurden verworfen — Pin nur in PR #7, Renovate auf main vorhanden. (c) Der TEMP-Block und die Supply-Chain-/mutable-Ref-Punkte tauchten mehrfach auf und wurden je einmal konsolidiert. - **Nicht pruefbar (klar markiert):** Die App-interne AuthN/AuthZ-Lage von TafForge (App-Code liegt im externen GitHub-Mirror, nicht in diesem Repo). Reales Laufzeitverhalten auf einem echten PVE-Host (`pct create`, In-LXC-Installer-Kette, mp0-Bind, whiptail-TTY-Pfade) — nur via DRY_RUN-Matrix abgedeckt, nicht real ausgefuehrt (OAM-Schutz; keine Builds/Installs/Deploys durchgefuehrt). - **Severity-Verteilung (nur confirmed+uncertain):** 0 critical, 2 high, 4 medium, 11 low, 2 info. Alle aufgenommenen Findings sind **confirmed**; keine uncertain verbleibend (zwei Halluzinationen/Fehlannahmen verworfen).
Author
Owner

Geschlossen beim Aufräumen am 01.09.2026.

Das hier ist ein Bericht, keine Aufgabe. Es ist eine Zustandsaufnahme des Repos vom 04.06.2026. Sie wurde in 89 Tagen kein einziges Mal kommentiert und beschreibt einen Codestand, den es so nicht mehr gibt.

Solche Berichte in der Liste der offenen Punkte zu führen, macht die Liste unbrauchbar: Von 571 offenen Einträgen über alle Repos waren 60 solche Berichte — jeder 10 bis 30 KB lang, keiner mit einer Aufgabe darin, die jemand abarbeiten könnte.

Es geht nichts verloren. Forgejo behält geschlossene Einträge vollständig; der Text bleibt hier lesbar und durchsuchbar.

Wenn aus dieser Analyse etwas offen ist, gehört es als eigener Eintrag mit einer klaren Aufgabe hierher — nicht als Bericht.

Geschlossen beim Aufräumen am 01.09.2026. **Das hier ist ein Bericht, keine Aufgabe.** Es ist eine Zustandsaufnahme des Repos vom 04.06.2026. Sie wurde in 89 Tagen kein einziges Mal kommentiert und beschreibt einen Codestand, den es so nicht mehr gibt. Solche Berichte in der Liste der offenen Punkte zu führen, macht die Liste unbrauchbar: Von 571 offenen Einträgen über alle Repos waren **60 solche Berichte** — jeder 10 bis 30 KB lang, keiner mit einer Aufgabe darin, die jemand abarbeiten könnte. **Es geht nichts verloren.** Forgejo behält geschlossene Einträge vollständig; der Text bleibt hier lesbar und durchsuchbar. Wenn aus dieser Analyse etwas offen ist, gehört es als eigener Eintrag mit einer klaren Aufgabe hierher — nicht als Bericht.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
vr6syncro/teddytafforge-proxmox#9
No description provided.