From f3385596399fa2a9cb089b425008784627e43f5c Mon Sep 17 00:00:00 2001 From: Dan Mahoney Date: Tue, 23 Jun 2026 01:05:27 -0400 Subject: [PATCH] feat: always log StaleMARC/suppressions-DB report skips Previously every skip reason (StaleMARC, the suppressions DB, and NoReportsList) was only printed under --verbose, so a report silently skipped via StaleMARC or the suppressions table left no trace in a normal cron run. NoReportsList stays --verbose-only since it's operator-curated and the operator already knows what's in it; the other two are dynamic/external and worth surfacing by default. --- reports/opendmarc-reports.8.in | 20 +++++++++++++++++--- reports/opendmarc-reports.in | 21 +++++++++++++++++---- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/reports/opendmarc-reports.8.in b/reports/opendmarc-reports.8.in index 43b4bb0..f06a425 100644 --- a/reports/opendmarc-reports.8.in +++ b/reports/opendmarc-reports.8.in @@ -302,13 +302,27 @@ Print version number and exit. The script checks the .I suppressions database table before sending each report. If the destination RUA/RUF -address, or its domain, appears in that table the report is silently -skipped. The table is also checked at the domain level, so adding a bare -domain name (e.g. +address, or its domain, appears in that table the report is skipped. The +table is also checked at the domain level, so adding a bare domain name +(e.g. .IR example.com ) suppresses all reports for that domain regardless of their destination address. Applies to both aggregate and forensic modes. .PP +Skips caused by the suppressions table or by +.I --stale-check +are always written to standard error, even without +.IR --verbose , +since these come from dynamic or external sources the operator may not be +expecting. Skips caused by +.B NoReportsList +or +.I --skipdomains +are not, since those are operator-curated and the operator already knows +what they contain; pass +.I --verbose +to see those as well. +.PP The table is loaded once at startup and is not queried per-message, so changes take effect on the next run. If the table does not exist (e.g. on a pre-migration install) the check is skipped and a notice is written to diff --git a/reports/opendmarc-reports.in b/reports/opendmarc-reports.in index 320cb5b..b79af0b 100755 --- a/reports/opendmarc-reports.in +++ b/reports/opendmarc-reports.in @@ -263,6 +263,18 @@ sub noreports_skip return 0; } +# StaleMARC and the suppressions DB are dynamic/external sources that can +# silently drop reports the operator wasn't expecting, so skips they cause +# are always worth printing. NoReportsList is operator-curated -- the +# operator already knows what's in it -- so it stays --verbose-only to +# avoid flooding output when it's used heavily. +sub loud_skip +{ + my ($reason) = @_; + + return $reason eq "StaleMARC" || $reason eq "suppression DB"; +} + # Returns the reason a destination address should be skipped (for logging), # or undef if it should be sent to. Combines every suppression source so # the result -- and therefore the recipients who actually get mail -- is @@ -688,7 +700,7 @@ if ($forensic) if (defined($reason)) { print STDERR "$progname: --forensic: skipping report for $reported_domain ($reason)\n" - if $verbose; + if $verbose || loud_skip($reason); exit($EXIT_SUPPRESSED); } } @@ -728,7 +740,7 @@ if ($forensic) @rcpts = grep { my $reason = skip_reason($_); print STDERR "$progname: --forensic: skipping $_ ($reason)\n" - if defined($reason) && $verbose; + if defined($reason) && ($verbose || loud_skip($reason)); !defined($reason); } @rcpts; @@ -1035,7 +1047,8 @@ foreach (@$domainset) my $domain_reason = domain_skip_reason($domain); if (defined($domain_reason)) { - print STDERR "$progname: skipping $domain ($domain_reason)\n" if $verbose; + print STDERR "$progname: skipping $domain ($domain_reason)\n" + if $verbose || loud_skip($domain_reason); next; } @@ -1632,7 +1645,7 @@ foreach (@$domainset) if (defined($reason)) { print STDERR "$progname: skipping $domain report to $repdest ($reason)\n" - if $verbose; + if $verbose || loud_skip($reason); next; } }