Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions cmd/eraser/cmd_audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ Examples:
type auditResult struct {
broker broker.Broker
verdict auditVerdict
// noChannel: dead email, unreachable website and no opt-out URL, so --fix
// would leave the entry with no way to contact the broker.
noChannel bool
}

func runAuditBrokers(regions, categories []string, timeout time.Duration, failOnDead, fix bool) error {
Expand Down Expand Up @@ -216,7 +219,13 @@ func auditConcurrently(targets []broker.Broker, checker *auditChecker, workers i
go func(i int, b broker.Broker) {
defer wg.Done()
defer func() { <-sem }()
results[i] = auditResult{broker: b, verdict: auditOne(b, checker)}
r := auditResult{broker: b, verdict: auditOne(b, checker)}
// auditOne stops at email-dead; check the site too so entries
// with nothing left get reported.
if r.verdict == verdictEmailDead && b.OptOutURL == "" && b.Website != "" {
r.noChannel = checkWebsiteVerdict(b.Website, checker) == verdictWebsiteDead
}
results[i] = r
}(i, b)
}
wg.Wait()
Expand Down Expand Up @@ -302,9 +311,12 @@ func hostOf(website string) string {

func printAuditResults(results []auditResult) {
counts := map[auditVerdict]int{}
var emailDead, websiteDead []string
var emailDead, websiteDead, noChannel []string
for _, r := range results {
counts[r.verdict]++
if r.noChannel {
noChannel = append(noChannel, r.broker.ID)
}
switch r.verdict {
case verdictEmailDead:
emailDead = append(emailDead, r.broker.ID)
Expand All @@ -328,6 +340,14 @@ func printAuditResults(results []auditResult) {
fmt.Printf(" - %s\n", id)
}
}
if len(noChannel) > 0 {
sort.Strings(noChannel)
fmt.Println()
fmt.Println("No working channel left (dead email, unreachable website, no opt-out URL) - consider removing:")
for _, id := range noChannel {
fmt.Printf(" - %s\n", id)
}
}
if len(websiteDead) > 0 {
sort.Strings(websiteDead)
fmt.Println()
Expand Down
16 changes: 16 additions & 0 deletions cmd/eraser/cmd_audit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,19 @@ type connRefusedErr struct{}
func (connRefusedErr) Error() string { return "connection refused" }

var errConnRefused = connRefusedErr{}

func TestAuditConcurrentlyFlagsNoChannel(t *testing.T) {
targets := []broker.Broker{
{ID: "gone", Email: "a@gone.test", Website: "https://gone.test"},
{ID: "has-portal", Email: "a@portal.test", Website: "https://portal.test", OptOutURL: "https://portal.test/optout"},
}
// MX/host lookups and HEAD all fail: both are email-dead, but only the
// one without an opt-out URL has nothing left.
results := auditConcurrently(targets, stubChecker(0, 0, 0, errConnRefused), 2)
if !results[0].noChannel {
t.Error("gone: want noChannel")
}
if results[1].noChannel {
t.Error("has-portal: opt-out URL remains, want !noChannel")
}
}
56 changes: 24 additions & 32 deletions data/brokers-verified.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
# Verified data-broker list - a smaller, higher-signal alternative to
# data/brokers.yaml. Every entry is a company we can confidently call a
# data broker (US: cross-checkable against state registries; EU: credit /
# data-intelligence bureaus) with a current first-party removal/privacy channel.
#
# Select it with `eraser send --list verified` or options.broker_list: verified.
# Grow it from registry CSVs - see docs/auditing.md.
brokers:
- id: 192-com
name: 192.com
Expand Down Expand Up @@ -90,7 +83,7 @@ brokers:
opt_out_url: https://www.data-axle.com/do-not-sell-my-data/
region: us
category: marketing
notes: 'Infogroup is the former Data Axle identity and was merged into this entry; infogroup.com redirects here. Cross-checkable against the California CPPA data-broker registry.'
notes: Infogroup is the former Data Axle identity and was merged into this entry; infogroup.com redirects here. Cross-checkable against the California CPPA data-broker registry.
- id: epsilon
name: Epsilon Data Management
email: privacy@epsilon.com
Expand All @@ -114,7 +107,7 @@ brokers:
opt_out_url: https://www.experian.com/privacy/opting_out
region: global
category: marketing
notes: 'Includes Tapad, acquired by Experian; the defunct standalone Tapad contact was merged here. Well-known data broker with a published first-party privacy channel.'
notes: Includes Tapad, acquired by Experian; the defunct standalone Tapad contact was merged here. Well-known data broker with a published first-party privacy channel.
- id: fastpeoplesearch
name: FastPeopleSearch
email: privacy@fastpeoplesearch.com
Expand All @@ -138,7 +131,7 @@ brokers:
opt_out_url: https://www.unacast.com/opt-out
region: us
category: marketing
notes: 'GA-UC Holdings and Gravy Analytics now route through Unacast; consolidated under the current company and first-party privacy channel. Cross-checkable against the California CPPA data-broker registry.'
notes: GA-UC Holdings and Gravy Analytics now route through Unacast; consolidated under the current company and first-party privacy channel. Cross-checkable against the California CPPA data-broker registry.
- id: hireright
name: HireRight
email: dpo@hireright.com
Expand All @@ -161,7 +154,7 @@ brokers:
opt_out_url: https://www.intelius.com/privacy-center/
region: us
category: people-search
notes: 'Intelius LLC also operates ZabaSearch and US Search; their duplicate targets were merged here. Cross-checkable against the California CPPA data-broker registry.'
notes: Intelius LLC also operates ZabaSearch and US Search; their duplicate targets were merged here. Cross-checkable against the California CPPA data-broker registry.
- id: kbm-group
name: KBM GROUP LLC
email: DoNotMail@wundermanthompson.com
Expand Down Expand Up @@ -295,7 +288,7 @@ brokers:
opt_out_url: https://instantdata.atdata.com/optout
region: us
category: marketing
notes: 'TowerData and FreshAddress merged and rebranded as AtData in 2021; Experian acquired AtData in 2026, but AtData still publishes this first-party privacy channel. Cross-checkable against the California CPPA data-broker registry.'
notes: TowerData and FreshAddress merged and rebranded as AtData in 2021; Experian acquired AtData in 2026, but AtData still publishes this first-party privacy channel. Cross-checkable against the California CPPA data-broker registry.
- id: transunion-marketing
name: TransUnion Marketing
email: privacy@transunion.com
Expand Down Expand Up @@ -333,21 +326,21 @@ brokers:
website: https://www.smartclip.tv/
region: eu
category: marketing
notes: 'Added 2026-08-20 from their privacy policy - DPO listed as Thomas Servatius, Ringstr. 16-20, 33378 Rheda-Wiedenbrueck, Germany. Not previously in the campaign.'
notes: Added 2026-08-20 from their privacy policy - DPO listed as Thomas Servatius, Ringstr. 16-20, 33378 Rheda-Wiedenbrueck, Germany. Not previously in the campaign.
- id: etarget-sk
name: eTarget s.r.o.
email: gdpr@etarget.sk
website: https://www.etarget.sk/
region: eu
category: marketing
notes: 'Added 2026-08-20 from their privacy policy contact section. Not previously in the campaign.'
notes: Added 2026-08-20 from their privacy policy contact section. Not previously in the campaign.
- id: schufa
name: SCHUFA Holding AG
email: datenschutz@schufa.de
website: https://www.schufa.de/
region: eu
category: financial-b2b
notes: 'Added 2026-09-12 from their GDPR/data-protection contact page - the major German consumer credit bureau. Their own materials note they steer requesters toward a paid "Bonitatsauskunft" product first; the free Art. 15 GDPR access/erasure request is a separate legal right.'
notes: Added 2026-09-12 from their GDPR/data-protection contact page - the major German consumer credit bureau. Their own materials note they steer requesters toward a paid "Bonitatsauskunft" product first; the free Art. 15 GDPR access/erasure request is a separate legal right.
- id: crif
name: CRIF S.p.A.
email: dirprivacy@crif.com
Expand All @@ -361,111 +354,110 @@ brokers:
website: https://www.dnb.com/en-gb/privacy/gdpr/
region: eu
category: financial-b2b
notes: 'Added 2026-09-12 from their EU GDPR contact page. D&B acquired Bisnode in 2021 and consolidated EU privacy requests under this EU DPO address; local Bisnode-branded offices route here too.'
notes: Added 2026-09-12 from their EU GDPR contact page. D&B acquired Bisnode in 2021 and consolidated EU privacy requests under this EU DPO address; local Bisnode-branded offices route here too.
- id: deutsche-post-direkt
name: Deutsche Post Direkt GmbH
email: datenschutz@postdirekt.de
website: https://www.deutschepost.de/de/d/deutsche-post-direkt.html
region: eu
category: marketing
notes: 'Added 2026-09-12 from their data-protection contact page - German address/geo-marketing data broker (Deutsche Post subsidiary).'
notes: Added 2026-09-12 from their data-protection contact page - German address/geo-marketing data broker (Deutsche Post subsidiary).
- id: az-direct
name: AZ Direct GmbH
email: datenschutz@az-direct.com
website: https://www.az-direct.com/
region: eu
category: marketing
notes: 'Added 2026-09-12 from their data-protection contact page - German/Austrian address broker (Bertelsmann group). A 2026 noyb complaint alleges AZ Direct has resisted providing full Art. 15 data-origin/recipient information, calling it "too burdensome" - a request may take persistence.'
notes: Added 2026-09-12 from their data-protection contact page - German/Austrian address broker (Bertelsmann group). A 2026 noyb complaint alleges AZ Direct has resisted providing full Art. 15 data-origin/recipient information, calling it "too burdensome" - a request may take persistence.
- id: adform
name: Adform A/S
email: dpo@adform.com
website: https://site.adform.com/
region: eu
category: requires-id
notes: 'Added 2026-09-12 from their Privacy Center - Danish ad-tech DSP/DMP. They ask for an ID document scan to verify identity before processing the request.'
notes: Added 2026-09-12 from their Privacy Center - Danish ad-tech DSP/DMP. They ask for an ID document scan to verify identity before processing the request.
- id: criteo
name: Criteo SA
email: dpo@criteo.com
website: https://www.criteo.com/
region: eu
category: marketing
notes: 'Added 2026-09-12 from their privacy policy - French retargeting/ad-tech company, one of the largest EU-based ad-tech data brokers.'
notes: Added 2026-09-12 from their privacy policy - French retargeting/ad-tech company, one of the largest EU-based ad-tech data brokers.
- id: rtb-house
name: RTB House S.A.
email: dpo@rtbhouse.com
website: https://www.rtbhouse.com/
region: eu
category: marketing
notes: 'Added 2026-09-12 from their privacy notices - Polish programmatic/retargeting ad-tech company.'
notes: Added 2026-09-12 from their privacy notices - Polish programmatic/retargeting ad-tech company.
- id: equativ
name: Equativ (formerly Smart AdServer)
email: dpo@equativ.com
website: https://equativ.com/
opt_out_url: https://privacy.equativ.com/user-rights-hub/
region: eu
category: marketing
notes: 'Added 2026-09-12 from their privacy documentation - French ad-serving/ad-tech company, rebranded from Smart AdServer. Updated to Equativ''s current DPO address and User Rights Hub.'
notes: Added 2026-09-12 from their privacy documentation - French ad-serving/ad-tech company, rebranded from Smart AdServer. Updated to Equativ's current DPO address and User Rights Hub.
- id: teads
name: Teads
email: privacy@teads.com
website: https://www.teads.com/
region: eu
category: marketing
notes: 'Added 2026-09-12 from their privacy policy - French outstream video ad-tech company (part of Outbrain). dpo@teads.com and dpo-uk@teads.com are alternates.'

notes: Added 2026-09-12 from their privacy policy - French outstream video ad-tech company (part of Outbrain). dpo@teads.com and dpo-uk@teads.com are alternates.
- id: nosis
name: 'Nosis Laboratorio de Investigación y Desarrollo S.A.'
name: Nosis Laboratorio de Investigación y Desarrollo S.A.
email: derechoacceso@nosis.com
website: https://www.nosis.com/
opt_out_url: https://www.nosis.com/es/legales/datos-personales
region: global
category: requires-id
notes: 'Argentine commercial/credit intelligence bureau (reports, scoring, "Big Data Personas" on ~40M Argentinians); Ley 25.326 data-rights channel with a direct address. DNI is required for rights requests.'
notes: Argentine commercial/credit intelligence bureau (reports, scoring, "Big Data Personas" on ~40M Argentinians); Ley 25.326 data-rights channel with a direct address. DNI is required for rights requests.
- id: cial-dun-bradstreet
name: CIAL Dun & Bradstreet
email: privacy@cialdnb.com
website: https://en.cialdnb.com/
opt_out_url: https://es.cialdnb.com/politica-de-privacidade
region: global
category: financial-b2b
notes: 'LATAM business-data/credit-intelligence network (43 countries, D&B affiliate in Argentina/Brazil/Mexico/Peru); privacy@cialdnb.com is their named DPO contact. Well-known data broker with a current first-party privacy channel.'
notes: LATAM business-data/credit-intelligence network (43 countries, D&B affiliate in Argentina/Brazil/Mexico/Peru); privacy@cialdnb.com is their named DPO contact. Well-known data broker with a current first-party privacy channel.
- id: circulo-de-credito
name: Círculo de Crédito S.A. de C.V. S.I.C.
email: ""
website: https://www.circulodecredito.com.mx/
opt_out_url: https://www.circulodecredito.com.mx/reclamacion
region: global
category: financial-b2b
notes: 'Mexican credit bureau regulated by SHCP/CNBV/Banxico/CONDUSEF. The first-party reclamation portal handles modification or deletion under the LRSIC; statutory retention limits apply.'
notes: Mexican credit bureau regulated by SHCP/CNBV/Banxico/CONDUSEF. The first-party reclamation portal handles modification or deletion under the LRSIC; statutory retention limits apply.
- id: buro-de-credito-mx
name: Buró de Crédito (Trans Union de México S.A. S.I.C.)
email: ""
website: https://www.burodecredito.com.mx/
opt_out_url: https://www.burodecredito.com.mx/personas-f%C3%ADsicas/productos/reporte-de-cr%C3%A9dito-especial/reclamaciones.html
region: global
category: financial-b2b
notes: 'Mexican LRSIC-regulated credit bureau with a current first-party reclamation channel. Official ID and supporting documents are required; statutory retention limits apply. Distinct controller from transunion-marketing.'
notes: Mexican LRSIC-regulated credit bureau with a current first-party reclamation channel. Official ID and supporting documents are required; statutory retention limits apply. Distinct controller from transunion-marketing.
- id: datacredito-experian-co
name: DataCrédito Experian Colombia
email: ""
website: https://www.datacredito.com.co/
opt_out_url: https://apps.datacredito.com.co/raw/ciudadanos/ciudadanos/web/indexReclamos
region: global
category: financial-b2b
notes: 'Colombian credit bureau operated by Experian Colombia S.A.; its first-party Habeas Data page routes claims through this SIC-regulated portal. Distinct Colombian controller from experian-marketing.'
notes: Colombian credit bureau operated by Experian Colombia S.A.; its first-party Habeas Data page routes claims through this SIC-regulated portal. Distinct Colombian controller from experian-marketing.
- id: centrix-nz
name: Centrix Group Limited
email: helpdesk@centrix.co.nz
website: https://www.centrix.co.nz/
opt_out_url: https://www.centrix.co.nz/my-credit-score/suppress-your-credit-file/
region: global
category: financial-b2b
notes: 'New Zealand consumer credit bureau (Credit Reporting Privacy Code 2020) with direct helpdesk and credit-report contacts plus a first-party credit-file suppression form.'
notes: New Zealand consumer credit bureau (Credit Reporting Privacy Code 2020) with direct helpdesk and credit-report contacts plus a first-party credit-file suppression form.
- id: metropol-crb
name: Metropol Credit Reference Bureau Limited
email: data@metropol.co.ke
website: https://metropol.co.ke/
opt_out_url: https://metropol.co.ke/privacy-policy-statement/
region: global
category: financial-b2b
notes: 'Kenyan credit bureau regulated by the Central Bank of Kenya, with a named Data Privacy Office. Erasure requests route to disputes@metropol.co.ke; CRB Regulations may require retention.'
notes: Kenyan credit bureau regulated by the Central Bank of Kenya, with a named Data Privacy Office. Erasure requests route to disputes@metropol.co.ke; CRB Regulations may require retention.
7 changes: 0 additions & 7 deletions data/brokers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -853,13 +853,6 @@ brokers:
website: https://www.bridgevine.com/
region: us
category: marketing
- id: brigthswipe
name: Brigthswipe Inc
email: ""
website: https://www.brightswipe.com/
region: us
category: marketing
notes: 'Brigthswipe Inc is BrightCheck''s registered corporate name (product/consumer brand vs legal entity, confirmed via a PRNewswire release announcing the BrightCheck launch) - the separate brightcheck entry was merged into this one. Email admin@brightswipe.com became undeliverable (audit-brokers: MX and host lookup both failed, 2026-10-12) - cleared, needs a working address.'
- id: brooks-integrated-marketing
name: Brooks Integrated Marketing, LLC
email: privacy@brooksim.com
Expand Down
13 changes: 10 additions & 3 deletions docs/auditing.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,16 @@ Two checks, both also wired into CI:
appending a dated note - then the workflow opens a PR against `data/brokers.yaml`
if anything changed. A quiet week opens no PR. Review the PR before merging;
investigate `website-dead` / `unknown` entries (printed in the run log, never
auto-edited) by hand. Also runnable from the Actions tab on demand. Run
`eraser audit-brokers --fix` locally with `--brokers <path>` to do the same to a
working copy.
auto-edited) by hand. A "No working channel left" section lists entries whose
email is dead *and* whose website is unreachable with no `opt_out_url` - remove
those by hand rather than keeping a row nobody can contact. Also runnable from
the Actions tab on demand. Run `eraser audit-brokers --fix` locally with
`--brokers <path>` to do the same to a working copy.
- **Canonical formatting** - `--fix` (and the bounce tooling) save with
`yaml.Marshal`, so a hand-edited list that isn't already in that form is
reformatted wholesale on the next audit PR. `TestEmbeddedListsCanonical` fails
until it is; `go test ./internal/broker -run TestEmbeddedListsCanonical -update`
rewrites `data/brokers*.yaml` in place.

## Security/Correctness Sweep (2026-08)

Expand Down
Loading
Loading