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

Open
opened 2026-06-04 01:45:10 +02:00 by vr6syncro · 0 comments
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.shmisc/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:417update-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,37shellcheck/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-652WRAPPER_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-52ct/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).
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.