Filter first-party domains from tracker mapping

Tracker scripts loaded through a first-party reverse proxy (e.g.
t.probo.com proxying PostHog) share the scanned site's eTLD+1 and
were incorrectly matched against the site owner's own
CommonThirdParty entry in matchByDomain. This caused trackers like
ph_phc_* to be attributed to the site owner instead of PostHog.

Load the CookieBanner origin in Process and pass it to both
matchByDomain and identifyWithAgent. Both now filter out initiator
domains whose eTLD+1 matches the site before querying the catalog
or feeding domains to the LLM agent. The prompt is also updated to
warn about proxy domains.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-05-28 22:39:13 +02:00
parent 979486020e
commit 9dfd04b449
4 changed files with 122 additions and 4 deletions

View File

@@ -102,3 +102,26 @@ func ExtractDomain(rawURL string) string {
return domain
}
// FilterFirstPartyDomains removes domains that match the eTLD+1 of
// siteOrigin. Tracker scripts loaded through a first-party proxy (e.g.
// t.probo.com proxying PostHog on a probo.com site) share the site's
// eTLD+1 and carry no signal about the actual third party. siteOrigin
// is a full URL such as "https://app.probo.com". The input domains are
// expected to be eTLD+1 strings (as produced by ExtractDomain).
func FilterFirstPartyDomains(domains []string, siteOrigin string) []string {
siteDomain := ExtractDomain(siteOrigin)
if siteDomain == "" {
return domains
}
filtered := make([]string, 0, len(domains))
for _, d := range domains {
if d != siteDomain {
filtered = append(filtered, d)
}
}
return filtered
}

View File

@@ -291,3 +291,76 @@ func TestExtractDomain(t *testing.T) {
)
}
}
func TestFilterFirstPartyDomains(t *testing.T) {
t.Parallel()
tests := []struct {
name string
domains []string
siteOrigin string
want []string
}{
{
name: "removes site domain from proxy",
domains: []string{"probo.com", "posthog.com"},
siteOrigin: "https://app.probo.com",
want: []string{"posthog.com"},
},
{
name: "keeps all third-party domains",
domains: []string{"stripe.com", "google.com"},
siteOrigin: "https://app.probo.com",
want: []string{"stripe.com", "google.com"},
},
{
name: "removes only matching domain",
domains: []string{"example.com", "googletagmanager.com", "example.com"},
siteOrigin: "https://www.example.com",
want: []string{"googletagmanager.com"},
},
{
name: "all domains are first party",
domains: []string{"probo.com"},
siteOrigin: "https://t.probo.com",
want: []string{},
},
{
name: "empty domains list",
domains: []string{},
siteOrigin: "https://probo.com",
want: []string{},
},
{
name: "nil domains list",
domains: nil,
siteOrigin: "https://probo.com",
want: []string{},
},
{
name: "invalid site origin preserves all",
domains: []string{"probo.com", "stripe.com"},
siteOrigin: "not-a-url",
want: []string{"probo.com", "stripe.com"},
},
{
name: "empty site origin preserves all",
domains: []string{"probo.com", "stripe.com"},
siteOrigin: "",
want: []string{"probo.com", "stripe.com"},
},
{
name: "co.uk site origin",
domains: []string{"example.co.uk", "analytics.google.com"},
siteOrigin: "https://shop.example.co.uk",
want: []string{"analytics.google.com"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
assert.Equal(t, tt.want, FilterFirstPartyDomains(tt.domains, tt.siteOrigin))
})
}
}