From 6487032461525aac053107662dd3d3b73ee9085c Mon Sep 17 00:00:00 2001 From: dtoro Date: Wed, 29 Jul 2026 19:45:12 +0200 Subject: [PATCH] fix(remote): ignore polluted host attributes; audit surfaces them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolveProxmoxHostSlug trusted attributes.host verbatim, so a value polluted with prose — lxc:teddycloud carried host="hubris (confirmed via pct config…)" — became a slug that never resolved, leaving its checks 'down' despite a correct `hosts` edge. Treat an attribute containing whitespace/parens as invalid and fall back to the canonical hosts edge. The audit now reports `polluted_attrs` — entities whose routing-critical attributes carry prose — so this class is visible instead of a silent resolution failure. --- internal/audit/audit.go | 8 ++++++++ internal/remote/remote.go | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/internal/audit/audit.go b/internal/audit/audit.go index be30051..d6055de 100644 --- a/internal/audit/audit.go +++ b/internal/audit/audit.go @@ -88,6 +88,14 @@ func Report(ctx context.Context, pool *db.Pool) ([]Finding, Summary) { AND src.state NOT IN ('destroyed','deprecated') AND tgt.state IN ('destroyed','deprecated')`, }, + { + Finding{Category: "polluted_attrs", Severity: "warning", + Evidence: "routing-critical attributes carrying prose (breaks resolution) — e.g. host='hubris (confirmed via pct…')", + SuggestedRunbook: "knowledge-graph-audit"}, + `SELECT slug || ': host=' || (attributes->>'host') FROM entities + WHERE attributes->>'host' IS NOT NULL + AND (attributes->>'host') ~ '[ (]'`, + }, } findings := make([]Finding, 0, len(specs)) diff --git a/internal/remote/remote.go b/internal/remote/remote.go index 956b805..6271ee1 100644 --- a/internal/remote/remote.go +++ b/internal/remote/remote.go @@ -104,6 +104,14 @@ func ResolveHost(ctx context.Context, pool *db.Pool, hostSlug, fallbackUser stri // directly rather than a slug subquery. func ResolveProxmoxHostSlug(ctx context.Context, pool *db.Pool, entityID uuid.UUID, hostAttr string) string { hostSlug := strings.TrimSpace(hostAttr) + // Only trust a clean token as a host name. The attribute is operator/ + // agent-writable and has been polluted with prose before ("hubris + // (confirmed via pct config…)") — using that verbatim produces a slug that + // never resolves. Treat anything with whitespace or parens as invalid and + // fall back to the canonical `hosts` edge below. + if strings.ContainsAny(hostSlug, " \t()") { + hostSlug = "" + } if hostSlug == "" { var relHostSlug string if err := pool.QueryRow(ctx, `