Skip to content

harden(portal): Pi-hole-Portal footgun-Review-Befunde (v5-Isolation + Härtung) - #194

Merged
CallMeTechie merged 1 commit into
masterfrom
fix/pihole-portal-footgun-findings
Jun 26, 2026
Merged

CallMeTechie merged 1 commit into
masterfrom
fix/pihole-portal-footgun-findings

Conversation

@CallMeTechie

Copy link
Copy Markdown
Owner

Härtung: Pi-hole-Portal (footgun-Review-Befunde)

Nachbesserung der von einer mehrstufigen JS-Review (footgun) gefundenen Punkte am Pi-hole-Portal (TP2a+TP2b). Verhaltens-erhaltend; keine Gate-/Präzedenz-/Leak-Semantik geändter. portalIdentity unangetastet.

Behoben

  • (major) piholeSync.js — Pi-hole-v5-Isolation: client.getTopClients(true) lag im Promise.all; auf v5 (FTL ohne den v6-Endpoint) warf der Call und riss die ganze Instanz auf connected:false (alle übrigen Daten verloren). Jetzt .catch(() => []) → nur topClientsBlocked degradiert still zu [], die Instanz bleibt voll funktional. Neuer Test pihole_sync_v5_degrade.test.js (2 Fälle).
  • (minor) portal.js route — Guard-Dedup: der dreifach kopierte License/Cache-Guard in einen Helper piholeUnavailable(cache) extrahiert (3 Handler).
  • (minor) portal.js widget — hydratePiholeScope entflochten: Render in benannte (test-sichere innere) Funktionen renderPiholeReason/renderPiholeStats aufgeteilt; Verhalten + DOM-Sicherheit + Leak-Guard identisch.
  • (nit) portal.js widget — Proto-sicherer Scope-Lookup: Whitelist statt PI_ENDPOINTS[scope] (verhindert __proto__/constructor-TypeError).
  • (nit) portal.js route — Aggregations-Pfad lesbar: gepackte Zeile + inline-for-Bodies entzerrt (security-sensitiver Pfad auditierbar).
  • (nit) portal.js widget — Initial-Scope über piScopeActive statt hartkodiertem 'device'.
  • (minor, security) portalOwner.js — Kiosk-Trade-off dokumentiert: der Shared-Peer-Fall des Trust-Schalters ist bewusstes Design (Default aus + Admin-Opt-in + Pflicht-Help-Text, Design §4.6); im Code als Kommentar festgehalten — kein Verhaltensbruch (das wäre eine Feature-Aushebelung).

Bewusst NICHT umgesetzt (begründet)

  • 2 Perf-Nits (Prepared-Statement modulweit cachen) abgelehnt. getDb() legt das DB-Handle lazy an und close() nullt es (Test-teardown()/Prod-Reconnect → neues Handle); ein gecachtes better-sqlite3-Statement liefe dann auf einem geschlossenen Handle → Use-after-close. getDb().prepare() pro Aufruf ist die repo-weite, sichere Konvention. Der vorgeschlagene „Fix" wäre selbst ein Bug.

Tests

Volle TP2a+TP2b-Regression grün (mit NODE_ENV=test für die piholeSync-Tests, CI-Standard); neuer v5-Degrade-Test grün; portalIdentity per git-diff unangetastet. Fokussiertes Review: Ready to merge (alle 7 Punkte verhaltens-erhaltend verifiziert).

🤖 Generated with Claude Code

https://claude.ai/code/session_01PrxALUszC9wFkYv1fedKyd

…-safe lookup

FIX 1 (piholeSync): catch() on getTopClients(true) so Pi-hole v5 instances
degrade topClientsBlocked to [] instead of marking the instance disconnected.

FIX 2 (portalOwner): add explanatory comment documenting the intentional
Kiosk trade-off on the device-trust branch (no behaviour change).

FIX 3 (api/portal): extract piholeUnavailable(cache) helper; replaces the
duplicated 3-clause guard in all three pihole handlers.

FIX 4 (public/portal.js): split hydratePiholeScope into two named inner
functions (renderPiholeReason, renderPiholeStats); test-safe via option (a).

FIX 5 (public/portal.js): whitelist scope before PI_ENDPOINTS lookup to
prevent proto-poisoning via DOM-supplied scope value.

FIX 8 (api/portal): expand packed aggregation lines in /pihole/owner
handler for readability; logic identical.

FIX 9 (public/portal.js): boot call uses piScopeActive instead of literal
'device' (same runtime value, forward-compatible).

New test: tests/pihole_sync_v5_degrade.test.js (2 cases, all pass).
Full suite: 42/42 green.
@CallMeTechie
CallMeTechie merged commit 37c18cf into master Jun 26, 2026
8 checks passed
@CallMeTechie
CallMeTechie deleted the fix/pihole-portal-footgun-findings branch June 26, 2026 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant