@@ -24,7 +24,7 @@ import {
2424 type ComplianceIssue ,
2525 registerValidators ,
2626} from "$lib/services/compliance" ;
27- import { parseBIMI } from "$lib/services/bimi" ;
27+ import { isBIMIDeclination , parseBIMI } from "$lib/services/bimi" ;
2828import { parseDMARC } from "$lib/services/dmarc" ;
2929
3030const SELECTOR_LABEL_RE = / ^ [ A - Z a - z 0 - 9 _ - ] + $ / ;
@@ -88,32 +88,50 @@ function bimiSync(raw: Record<string, any>, ctx: ComplianceContext): ComplianceI
8888 } ) ;
8989 }
9090
91- // l= (Location) is mandatory.
92- if ( ! val . l ) {
91+ // Declination: a domain that does not wish to participate publishes
92+ // v=BIMI1 with an empty l= tag. The URL/VMC checks no longer apply,
93+ // but the DMARC cross-check still does: a declining domain still has
94+ // to back the declination with an enforcing DMARC policy, otherwise
95+ // an attacker could spoof the domain and override the declination.
96+ const declination = isBIMIDeclination ( txtValue ) ;
97+ if ( declination ) {
9398 issues . push ( {
94- id : "bimi.missing-location" ,
95- severity : "error" ,
96- field : "l" ,
97- docUrl : DRAFT ,
98- } ) ;
99- } else if ( ! isHttps ( val . l ) ) {
100- issues . push ( {
101- id : "bimi.location-not-https" ,
102- severity : "error" ,
103- field : "l" ,
104- docUrl : DRAFT ,
105- } ) ;
106- } else if ( ! / \. s v g ( \? | # | $ ) / i. test ( val . l ) ) {
107- issues . push ( {
108- id : "bimi.location-not-svg" ,
109- severity : "warning" ,
110- field : "l" ,
99+ id : "bimi.declination" ,
100+ severity : "info" ,
111101 docUrl : DRAFT ,
112102 } ) ;
113103 }
114104
105+ // l= (Location) is mandatory outside of declination.
106+ if ( ! declination ) {
107+ if ( ! val . l ) {
108+ issues . push ( {
109+ id : "bimi.missing-location" ,
110+ severity : "error" ,
111+ field : "l" ,
112+ docUrl : DRAFT ,
113+ } ) ;
114+ } else if ( ! isHttps ( val . l ) ) {
115+ issues . push ( {
116+ id : "bimi.location-not-https" ,
117+ severity : "error" ,
118+ field : "l" ,
119+ docUrl : DRAFT ,
120+ } ) ;
121+ } else if ( ! / \. s v g ( \? | # | $ ) / i. test ( val . l ) ) {
122+ issues . push ( {
123+ id : "bimi.location-not-svg" ,
124+ severity : "warning" ,
125+ field : "l" ,
126+ docUrl : DRAFT ,
127+ } ) ;
128+ }
129+ }
130+
115131 // a= (Authority / VMC) is optional but strongly recommended.
116- if ( val . a ) {
132+ if ( declination ) {
133+ // No-op: VMC is meaningless on a declination record.
134+ } else if ( val . a ) {
117135 if ( ! isHttps ( val . a ) ) {
118136 issues . push ( {
119137 id : "bimi.authority-not-https" ,
@@ -139,7 +157,7 @@ function bimiSync(raw: Record<string, any>, ctx: ComplianceContext): ComplianceI
139157 }
140158
141159 // e= (Evidence) optional, must be HTTPS if present.
142- if ( val . e && ! isHttps ( val . e ) ) {
160+ if ( ! declination && val . e && ! isHttps ( val . e ) ) {
143161 issues . push ( {
144162 id : "bimi.evidence-not-https" ,
145163 severity : "warning" ,
0 commit comments