@redsift/products 12.5.9 → 12.5.10-muiv7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_internal/SignalCardSpfDomain.js +233 -18
- package/_internal/SignalCardSpfDomain.js.map +1 -1
- package/bimi-checker.d.ts +94 -94
- package/index.d.ts +306 -306
- package/package.json +6 -6
- package/signal-cards.d.ts +306 -306
|
@@ -5,7 +5,7 @@ import styled, { css } from 'styled-components';
|
|
|
5
5
|
import pako from 'pako';
|
|
6
6
|
import { interpolateTranslation, mapStatusToDetailedCardColor, isStructuredData, calculateSankeyHeight, create2LevelSankey, extractDmarcData, extractSpfData, extractTlsData, extractFcrdnsData, extractDnssecData, extractThreatIntelligenceData, extractUrlsData, extractBimiData, extractAmpData, extractSubdoData, extractDomainAnalyzerDkimData, extractDomainAnalyzerSpfData, extractDmarcDomainData, extractSpfDomainData, extractMtaStsFullData, extractDkimData, recordStatus } from '@redsift/signal-logic';
|
|
7
7
|
import { useLocalizedStringFormatter, useTheme, Button, Icon, DetailedCard, ButtonLink, Flexbox } from '@redsift/design-system';
|
|
8
|
-
import { mdiHelpCircle, mdiClose, mdiAlert, mdiEmail, mdiEarth, mdiInformationOutline, mdiCheck, mdiInformation } from '@redsift/icons';
|
|
8
|
+
import { mdiHelpCircle, mdiClose, mdiAlert, mdiEmail, mdiEarth, mdiInformationOutline, mdiCheck, mdiInformation, mdiFormatAlignRight, mdiFormatAlignJustify } from '@redsift/icons';
|
|
9
9
|
import { createPortal } from 'react-dom';
|
|
10
10
|
|
|
11
11
|
const COLOR = {
|
|
@@ -283,6 +283,8 @@ var EXTRACTOR_STRINGS = {
|
|
|
283
283
|
"card.dkim.unavailable-try-later_md": "Please try sending another email later.",
|
|
284
284
|
"card.dkim.unavailable_md": "**[](DKIM#:glossary) evaluation temporarily unavailable.**",
|
|
285
285
|
"card.dmarc.domain-aligned": "The domain is aligned with the From Domain. It can contribute to passing DMARC.",
|
|
286
|
+
"card.dmarc.domain-aligned-not-verified": "The domain is aligned with the From Domain, but its signature did not verify, so it cannot contribute to passing DMARC.",
|
|
287
|
+
"card.dmarc.spf-domain-aligned-not-verified": "The domain is aligned with the From Domain, but SPF did not pass, so it cannot contribute to passing DMARC.",
|
|
286
288
|
"card.dmarc.domain-not-aligned": "The domain is not aligned with the From Domain. It cannot contribute to passing DMARC.",
|
|
287
289
|
"card.dmarc.error.dns-timeout": "DNS timeout when looking up DMARC record",
|
|
288
290
|
"card.dmarc.error.invalid-pct-value": "Pct value must be between 0 and 100",
|
|
@@ -766,6 +768,8 @@ var EXTRACTOR_STRINGS = {
|
|
|
766
768
|
"card.dkim.unavailable-try-later_md": "Veuillez essayer d'envoyer un autre courriel plus tard.",
|
|
767
769
|
"card.dkim.unavailable_md": "**[](DKIM#:glossary) \xE9valuation temporairement indisponible.**",
|
|
768
770
|
"card.dmarc.domain-aligned": "Le domaine est align\xE9 avec le domaine exp\xE9diteur. Il peut contribuer \xE0 la r\xE9ussite de DMARC.",
|
|
771
|
+
"card.dmarc.domain-aligned-not-verified": "Le domaine est align\xE9 avec le domaine exp\xE9diteur, mais sa signature n'a pas \xE9t\xE9 v\xE9rifi\xE9e, il ne peut donc pas contribuer \xE0 la r\xE9ussite de DMARC.",
|
|
772
|
+
"card.dmarc.spf-domain-aligned-not-verified": "Le domaine est align\xE9 avec le domaine exp\xE9diteur, mais SPF n'a pas r\xE9ussi, il ne peut donc pas contribuer \xE0 la r\xE9ussite de DMARC.",
|
|
769
773
|
"card.dmarc.domain-not-aligned": "Le domaine n'est pas align\xE9 avec le domaine exp\xE9diteur. Il ne peut pas contribuer \xE0 la r\xE9ussite de DMARC.",
|
|
770
774
|
"card.dmarc.error.dns-timeout": "D\xE9lai d'attente DNS lors de la recherche de l'enregistrement DMARC",
|
|
771
775
|
"card.dmarc.error.invalid-pct-value": "La valeur pct doit \xEAtre entre 0 et 100",
|
|
@@ -1334,7 +1338,7 @@ var enUS = {
|
|
|
1334
1338
|
"investigate.card.dmarc.void-dynamic-dmarc-ns-record": "Dynamic DMARC record (NS) is added but Dynamic Services aren't enabled",
|
|
1335
1339
|
"investigate.card.dmarc.policy-none-pct-0_md": "No reports will be sent with this policy, because the sample rate [](PCT#:glossary) is set to `0`.",
|
|
1336
1340
|
"investigate.card.dmarc.policy-not-none-pct-0_md": "All **unauthorized** email will be delivered to receivers although the policy is set to [{policy}](DMARC_POLICY#:glossary), because the sample rate [](PCT#:glossary) is set to `0`.",
|
|
1337
|
-
"investigate.card.dmarc.policy-inherited-from-org-domain_md": "The DMARC policy for the [](FROM#:glossary) `{fromDomain}` comes from the [](ORGANIZATIONAL_DOMAIN#:glossary) `{orgDomain}`",
|
|
1341
|
+
"investigate.card.dmarc.policy-inherited-from-org-domain_md": "The DMARC policy for the [](FROM#:glossary) `{fromDomain}` comes from the [Organizational Domain](ORGANIZATIONAL_DOMAIN#:glossary) `{orgDomain}`",
|
|
1338
1342
|
"investigate.card.dmarc.category-policy-good_md": "The policy on your DNS conforms with the DMARC specification.",
|
|
1339
1343
|
"investigate.card.dmarc.all-emails-reported_md": "All emails will be [REPORTED](DMARC_POLICY#:glossary).",
|
|
1340
1344
|
"investigate.card.dmarc.all-emails-reported-only_md": "DMARC configured in report-only mode. All emails will only be [REPORTED](DMARC_POLICY#:glossary).",
|
|
@@ -1924,6 +1928,8 @@ var enUS = {
|
|
|
1924
1928
|
"investigate.section-titles.bimi-domain": "DMARC Domains",
|
|
1925
1929
|
"investigate.section-titles.dmarc-domain": "DMARC Domains",
|
|
1926
1930
|
"investigate.card.dmarc.domain-aligned": "The domain is aligned with the From Domain. It can contribute to passing DMARC.",
|
|
1931
|
+
"investigate.card.dmarc.domain-aligned-not-verified": "The domain is aligned with the From Domain, but its signature did not verify, so it cannot contribute to passing DMARC.",
|
|
1932
|
+
"investigate.card.dmarc.spf-domain-aligned-not-verified": "The domain is aligned with the From Domain, but SPF did not pass, so it cannot contribute to passing DMARC.",
|
|
1927
1933
|
"investigate.card.dmarc.domain-not-aligned": "The domain is not aligned with the From Domain. It cannot contribute to passing DMARC."
|
|
1928
1934
|
};
|
|
1929
1935
|
|
|
@@ -2022,11 +2028,11 @@ var frFR = {
|
|
|
2022
2028
|
"investigate.card.dmarc.policy-remaining-emails-pct-reject_md": "Les **{remainingPercent}%** restants de courriels non autorisés seront soumis à la politique de **mise en quarantaine**.",
|
|
2023
2029
|
"investigate.card.dmarc.policy-remaining-emails-pct-quarantine_md": "Les **{remainingPercent}%** restants de courriels non autorisés seront **livrés normalement**.",
|
|
2024
2030
|
"investigate.card.dmarc.no-policy_md": "**Aucune [FULL](DMARC_POLICY#:glossary) publiée sur le domaine**\n\n**`{policyDomain}`**",
|
|
2025
|
-
"investigate.card.dmarc.dmarc-passed_md": "[](DMARC#:glossary) **
|
|
2026
|
-
"investigate.card.dmarc.dmarc-passed-no-rua_md": "[](DMARC#:glossary) est conforme
|
|
2027
|
-
"investigate.card.dmarc.dmarc-no-rua_md": "[](DMARC#:glossary) n'a pas d'
|
|
2031
|
+
"investigate.card.dmarc.dmarc-passed_md": "[](DMARC#:glossary) **réussi**",
|
|
2032
|
+
"investigate.card.dmarc.dmarc-passed-no-rua_md": "[](DMARC#:glossary) est conforme à BIMI **MAIS** n'a pas d'**adresse de rapport agrégé** (rua).",
|
|
2033
|
+
"investigate.card.dmarc.dmarc-no-rua_md": "[](DMARC#:glossary) n'a pas d'**adresse de rapport agrégé** (rua).",
|
|
2028
2034
|
"investigate.card.dmarc.dmarc-invalid-record_md": "[](DMARC#:glossary) a un enregistrement **invalide** .",
|
|
2029
|
-
"investigate.card.dmarc.dmarc-failed_md": "[](DMARC#:glossary) **ne sont pas** conformes
|
|
2035
|
+
"investigate.card.dmarc.dmarc-failed_md": "Les paramètres [](DMARC#:glossary) **ne sont pas** conformes à BIMI",
|
|
2030
2036
|
"investigate.card.dmarc.misplaced-policy_md": "**Le [FULL](DMARC_POLICY#:glossary) est mal placé.",
|
|
2031
2037
|
"investigate.card.dmarc.misplaced-bad-policy_md": "**Le [FULL](DMARC_POLICY#:glossary) est mal placé et a une erreur de syntaxe:**",
|
|
2032
2038
|
"investigate.card.dmarc.bad-policy_md_one": "**Le [FULL](DMARC_POLICY#:glossary) présente l'erreur de syntaxe suivante:**.",
|
|
@@ -2049,7 +2055,7 @@ var frFR = {
|
|
|
2049
2055
|
"investigate.card.dmarc.void-dynamic-dmarc-ns-record": "L'enregistrement DMARC dynamique (NS) est ajouté mais les Dynamic Services ne sont pas activés.",
|
|
2050
2056
|
"investigate.card.dmarc.policy-none-pct-0_md": "Aucun rapport ne sera envoyé avec cette politique, car le taux d'échantillonnage [](PCT#:glossary) est fixé à `0`.",
|
|
2051
2057
|
"investigate.card.dmarc.policy-not-none-pct-0_md": "Tous les emails **non autorisés** seront délivrés aux destinataires bien que la politique soit définie sur [{policy}](DMARC_POLICY#:glossary), parce que le taux d'échantillonnage [](PCT#:glossary) est défini sur `0`.",
|
|
2052
|
-
"investigate.card.dmarc.policy-inherited-from-org-domain_md": "La politique DMARC du [](FROM#:glossary) `{fromDomain}` provient du [](ORGANIZATIONAL_DOMAIN#:glossary) `{orgDomain}`",
|
|
2058
|
+
"investigate.card.dmarc.policy-inherited-from-org-domain_md": "La politique DMARC du [](FROM#:glossary) `{fromDomain}` provient du [domaine organisationnel](ORGANIZATIONAL_DOMAIN#:glossary) `{orgDomain}`",
|
|
2053
2059
|
"investigate.card.dmarc.category-policy-good_md": "La politique de votre DNS est conforme à la spécification DMARC.",
|
|
2054
2060
|
"investigate.card.dmarc.all-emails-reported_md": "Tous les courriels seront [REPORTED](DMARC_POLICY#:glossary).",
|
|
2055
2061
|
"investigate.card.dmarc.all-emails-reported-only_md": "DMARC configuré en mode rapport uniquement. Tous les courriels seront uniquement [REPORTED](DMARC_POLICY#:glossary).",
|
|
@@ -2104,21 +2110,21 @@ var frFR = {
|
|
|
2104
2110
|
"investigate.card.subdo.blog": "Vous pouvez obtenir un aperçu complet de votre configuration SPF sur notre vérificateur SPF, ou en savoir plus sur l'attaque SubdoMailing sur notre base de connaissances.",
|
|
2105
2111
|
"investigate.card.subdo.spf-subdo-poisoned-include_md": "Inclusion compromise : **{raw}**",
|
|
2106
2112
|
"investigate.card.subdo.spf-subdo-poisoned-mechanism_md": "Inclusion compromise : **{raw}**",
|
|
2107
|
-
"investigate.card.spf.spf-no-spf_md": "
|
|
2113
|
+
"investigate.card.spf.spf-no-spf_md": "Aucun SPF configuré pour le domaine.",
|
|
2108
2114
|
"investigate.card.spf.spf-ipf-error-spf_md": "Erreur SPF : {msg}",
|
|
2109
2115
|
"investigate.card.spf.spf-extra-spf-lookups_md": "Trop de recherches SPF ({lookupCount} trouvées, 10 autorisées)",
|
|
2110
2116
|
"investigate.card.spf.spf-multiple-mx-providers_md": "Plusieurs fournisseurs de messagerie configurés dans les enregistrements MX de ce domaine.",
|
|
2111
|
-
"investigate.card.spf.spf-multiple-spf_md": "Plusieurs enregistrements SPF
|
|
2112
|
-
"investigate.card.spf.spf-multiple-all_md": "Plusieurs mécanismes **
|
|
2113
|
-
"investigate.card.spf.spf-recursive-include-spf_md": "L'inclusion SPF redondante peut être supprimée car elle
|
|
2114
|
-
"investigate.card.spf.spf-v-tag-duplicated-spf_md": "La balise de
|
|
2115
|
-
"investigate.card.spf.spf-all-not-last_md": "Toute donnée
|
|
2116
|
-
"investigate.card.spf.spf-all-and-redirect_md": "Toute **redirection** est ignorée s'il
|
|
2117
|
-
"investigate.card.spf.spf-all-plus-warning_md": "Le mécanisme **+all** ne
|
|
2117
|
+
"investigate.card.spf.spf-multiple-spf_md": "Plusieurs enregistrements SPF trouvés pour ce domaine",
|
|
2118
|
+
"investigate.card.spf.spf-multiple-all_md": "Plusieurs mécanismes **all** dans l'enregistrement SPF",
|
|
2119
|
+
"investigate.card.spf.spf-recursive-include-spf_md": "L'inclusion SPF redondante peut être supprimée car elle pointe vers votre propre domaine",
|
|
2120
|
+
"investigate.card.spf.spf-v-tag-duplicated-spf_md": "La balise de version est dupliquée.",
|
|
2121
|
+
"investigate.card.spf.spf-all-not-last_md": "Toute donnée après le **all** est ignorée.",
|
|
2122
|
+
"investigate.card.spf.spf-all-and-redirect_md": "Toute **redirection** est ignorée s'il y a un mécanisme **all**.",
|
|
2123
|
+
"investigate.card.spf.spf-all-plus-warning_md": "Le mécanisme **+all** ne devrait pas être utilisé car il autorise toutes les adresses IP d'envoi.",
|
|
2118
2124
|
"investigate.card.spf.spf-all-question-mark-warning_md": "**?all** ne doit pas être utilisé car il permet une réponse ambiguë.",
|
|
2119
|
-
"investigate.card.spf.spf-no-all_md": "Il est recommandé d'utiliser un modificateur **redirect**
|
|
2120
|
-
"investigate.card.spf.spf-invalid-spf1-header_md": "Enregistrement SPF
|
|
2121
|
-
"investigate.card.spf.spf-invalid-spf-header-include_md": "L'enregistrement SPF
|
|
2125
|
+
"investigate.card.spf.spf-no-all_md": "Il est recommandé d'utiliser soit un modificateur **redirect** soit un mécanisme **all** pour terminer explicitement le traitement.",
|
|
2126
|
+
"investigate.card.spf.spf-invalid-spf1-header_md": "Enregistrement SPF invalide",
|
|
2127
|
+
"investigate.card.spf.spf-invalid-spf-header-include_md": "L'enregistrement SPF est invalide car il a un espace entre l'instruction include:.",
|
|
2122
2128
|
"investigate.card.spf.spf-ip-subnet-mask-too-small_md": "Le sous-réseau **{raw}** permet à un grand nombre d'hôtes d'envoyer du courrier électronique au nom du domaine.",
|
|
2123
2129
|
"investigate.card.spf.spf-ip-subnet-overlaps-public-range_md": "Le sous-réseau **{raw}** chevauche la plage **{value}**, appartenant à un fournisseur de cloud accessible au public : **{sourceRef}**.",
|
|
2124
2130
|
"investigate.card.spf.spf-directive-permissive-exists_md": "Le mécanisme **{raw}** a été défini pour permettre à n'importe quel hôte d'envoyer un courrier électronique au nom du domaine.",
|
|
@@ -2641,6 +2647,8 @@ var frFR = {
|
|
|
2641
2647
|
"investigate.section-titles.bimi-domain": "Domaines DMARC",
|
|
2642
2648
|
"investigate.section-titles.dmarc-domain": "Domaines DMARC",
|
|
2643
2649
|
"investigate.card.dmarc.domain-aligned": "Le domaine est aligné avec le domaine expéditeur. Il peut contribuer à la réussite de DMARC.",
|
|
2650
|
+
"investigate.card.dmarc.domain-aligned-not-verified": "Le domaine est aligné avec le domaine expéditeur, mais sa signature n'a pas été vérifiée, il ne peut donc pas contribuer à la réussite de DMARC.",
|
|
2651
|
+
"investigate.card.dmarc.spf-domain-aligned-not-verified": "Le domaine est aligné avec le domaine expéditeur, mais SPF n'a pas réussi, il ne peut donc pas contribuer à la réussite de DMARC.",
|
|
2644
2652
|
"investigate.card.dmarc.domain-not-aligned": "Le domaine n'est pas aligné avec le domaine expéditeur. Il ne peut pas contribuer à la réussite de DMARC."
|
|
2645
2653
|
};
|
|
2646
2654
|
|
|
@@ -6982,6 +6990,206 @@ function renderList(items) {
|
|
|
6982
6990
|
}, item)));
|
|
6983
6991
|
}
|
|
6984
6992
|
|
|
6993
|
+
// =============================================================================
|
|
6994
|
+
// STATUS LIST RENDERING
|
|
6995
|
+
// =============================================================================
|
|
6996
|
+
|
|
6997
|
+
/**
|
|
6998
|
+
* Status-list container. Mirrors the plain `<ul>` that `renderList` emits so the
|
|
6999
|
+
* two list variants sit at the same indent and rhythm, adding only the per-entry
|
|
7000
|
+
* row layout the status icons need.
|
|
7001
|
+
*/
|
|
7002
|
+
const StyledStatusList = styled.ul`
|
|
7003
|
+
li {
|
|
7004
|
+
display: flex;
|
|
7005
|
+
align-items: center;
|
|
7006
|
+
gap: 0.35em;
|
|
7007
|
+
min-width: 0;
|
|
7008
|
+
}
|
|
7009
|
+
`;
|
|
7010
|
+
|
|
7011
|
+
/**
|
|
7012
|
+
* The entry's text (a domain, on the alignment row).
|
|
7013
|
+
*
|
|
7014
|
+
* `min-width: 0` has to be on the text itself, not just the `li`: a flex item
|
|
7015
|
+
* defaults to `min-width: auto`, which refuses to shrink below its content's
|
|
7016
|
+
* intrinsic width, so a long unbroken domain would push the status icon
|
|
7017
|
+
* (`flex-shrink: 0`) out of the row. `overflow-wrap: anywhere` lets a label with no
|
|
7018
|
+
* natural break opportunity wrap instead of overflowing.
|
|
7019
|
+
*
|
|
7020
|
+
* A dedicated component rather than an `li > span` descendant selector, which would
|
|
7021
|
+
* also be matched by the strike wrapper if the markup were ever reshuffled.
|
|
7022
|
+
*/
|
|
7023
|
+
const StyledStatusListText = styled.span`
|
|
7024
|
+
min-width: 0;
|
|
7025
|
+
overflow-wrap: anywhere;
|
|
7026
|
+
`;
|
|
7027
|
+
|
|
7028
|
+
// A div, not a span: TooltipInPortalWrapper renders a <div> (TooltipInPortal.tsx),
|
|
7029
|
+
// and a <div> inside a <span> is invalid HTML — span is phrasing content, div is
|
|
7030
|
+
// flow content. `display: inline-flex` keeps it laid out inline regardless. This
|
|
7031
|
+
// matches the row-level alignment icon in SignalCardExtractedItems, which wraps
|
|
7032
|
+
// the same tooltip in a div for the same reason.
|
|
7033
|
+
const StyledStatusListIcon = styled.div`
|
|
7034
|
+
display: inline-flex;
|
|
7035
|
+
align-items: center;
|
|
7036
|
+
flex-shrink: 0;
|
|
7037
|
+
`;
|
|
7038
|
+
|
|
7039
|
+
/**
|
|
7040
|
+
* Diagonal strike across a status icon, for the not-aligned case.
|
|
7041
|
+
*
|
|
7042
|
+
* Drawn in CSS over the mdi glyph rather than inlining a bespoke crossed-out SVG,
|
|
7043
|
+
* so it scales with the glyph and the DS keeps a single source for the underlying
|
|
7044
|
+
* icon.
|
|
7045
|
+
*
|
|
7046
|
+
* The colour is declared **here, once**, on the wrapper: the `::after` gradient
|
|
7047
|
+
* picks it up through `currentColor`, and the `Icon` inside is passed
|
|
7048
|
+
* `currentColor` too, so glyph and strike cannot drift apart. Setting it on the
|
|
7049
|
+
* pseudo-element instead would make `currentColor` resolve to that local
|
|
7050
|
+
* declaration rather than being inherited, which is the trap this replaced.
|
|
7051
|
+
*/
|
|
7052
|
+
const StyledStruckThrough = styled.span`
|
|
7053
|
+
position: relative;
|
|
7054
|
+
display: inline-flex;
|
|
7055
|
+
align-items: center;
|
|
7056
|
+
color: ${COLOR.main.red};
|
|
7057
|
+
|
|
7058
|
+
&::after {
|
|
7059
|
+
content: '';
|
|
7060
|
+
position: absolute;
|
|
7061
|
+
inset: 0;
|
|
7062
|
+
background: linear-gradient(
|
|
7063
|
+
to top right,
|
|
7064
|
+
transparent calc(50% - 0.7px),
|
|
7065
|
+
currentColor calc(50% - 0.7px),
|
|
7066
|
+
currentColor calc(50% + 0.7px),
|
|
7067
|
+
transparent calc(50% + 0.7px)
|
|
7068
|
+
);
|
|
7069
|
+
pointer-events: none;
|
|
7070
|
+
}
|
|
7071
|
+
`;
|
|
7072
|
+
|
|
7073
|
+
/**
|
|
7074
|
+
* Status icon for a single {@link StatusListItem}.
|
|
7075
|
+
*
|
|
7076
|
+
* Mirrors the icon vocabulary `getIcon` uses for row-level statuses in
|
|
7077
|
+
* SignalCardExtractedItems, so a per-entry tick reads identically to a row-level
|
|
7078
|
+
* one. A `''` status means "no status" — the entry renders as plain text, exactly
|
|
7079
|
+
* as a `ListData` entry would.
|
|
7080
|
+
*
|
|
7081
|
+
* Per Antony's spec (2026-08-06) this row uses an **align** glyph rather than a
|
|
7082
|
+
* generic tick/cross, with the not-aligned case struck through:
|
|
7083
|
+
*
|
|
7084
|
+
* | status | `matchMode` | meaning | render |
|
|
7085
|
+
* | --- | --- | --- | --- |
|
|
7086
|
+
* | `good`/`warning` | `exact` | signing domain equals the From domain | green `FormatAlignJustify` |
|
|
7087
|
+
* | `good`/`warning` | `organizational` | shares the Organizational Domain | green `FormatAlignRight` |
|
|
7088
|
+
* | `danger` | — | does not align | red `FormatAlignRight`, struck through |
|
|
7089
|
+
*
|
|
7090
|
+
* **`warning` renders as aligned, not amber.** This row is about *domains*, not
|
|
7091
|
+
* mechanism results (Antony, option (a), 2026-08-06). signal-logic's `warning`
|
|
7092
|
+
* branch fires on `aligned && !verified`, where `verified` is
|
|
7093
|
+
* `signature.code === 'pass'` — a property of the crypto, not of the domain pair —
|
|
7094
|
+
* and the DKIM card already reports verification per signature. Colouring it
|
|
7095
|
+
* differently here would make one icon answer two questions and duplicate the DKIM
|
|
7096
|
+
* card, inviting the two to disagree. The distinct `tooltipKey`
|
|
7097
|
+
* (`card.dmarc.domain-aligned-not-verified`) is preserved, so the detail is still
|
|
7098
|
+
* available on hover.
|
|
7099
|
+
*
|
|
7100
|
+
* The known cost: an aligned-but-unverified domain cannot actually contribute to
|
|
7101
|
+
* DMARC passing, so green slightly overstates it. That is conveyed by the
|
|
7102
|
+
* "Alignment Check" prose rows and the overall card verdict, which read as failed —
|
|
7103
|
+
* the honest place for it, rather than overloading a per-domain icon.
|
|
7104
|
+
*
|
|
7105
|
+
* **Exact vs organizational** comes from `matchMode`, added upstream by WEB-1203.
|
|
7106
|
+
* An exact match is *inherently* aligned under either `adkim` mode, so it earns the
|
|
7107
|
+
* fully-justified glyph; an Organizational Domain match only aligns under `adkim=r`
|
|
7108
|
+
* and gets the right-aligned one. `matchMode` is absent on non-aligned entries and
|
|
7109
|
+
* on the fallback `ListData` shape, so a missing value degrades to `FormatAlignRight`
|
|
7110
|
+
* rather than guessing.
|
|
7111
|
+
*/
|
|
7112
|
+
function renderItemStatusIcon(status, matchMode, accessibleName) {
|
|
7113
|
+
// The DS `Icon` is decorative by default — it sets aria-hidden unless given an
|
|
7114
|
+
// aria-label. The status here is not decorative: it is the only thing telling a
|
|
7115
|
+
// reader whether the domain aligned, and the tooltip carrying that meaning is
|
|
7116
|
+
// hover-only. So pass the translated tooltip text as the accessible name, with
|
|
7117
|
+
// `aria-hidden={false}` to un-hide it, and fall back to the bare status word
|
|
7118
|
+
// when an entry supplies no tooltipKey.
|
|
7119
|
+
const label = accessibleName !== null && accessibleName !== void 0 ? accessibleName : status || undefined;
|
|
7120
|
+
const aria = label ? {
|
|
7121
|
+
'aria-label': label,
|
|
7122
|
+
'aria-hidden': false
|
|
7123
|
+
} : {};
|
|
7124
|
+
switch (status) {
|
|
7125
|
+
// `warning` is deliberately identical to `good` — see above.
|
|
7126
|
+
case 'good':
|
|
7127
|
+
case 'warning':
|
|
7128
|
+
return /*#__PURE__*/React__default.createElement(Icon, _extends({
|
|
7129
|
+
icon: matchMode === 'exact' ? mdiFormatAlignJustify : mdiFormatAlignRight,
|
|
7130
|
+
color: COLOR.main.green,
|
|
7131
|
+
size: "small"
|
|
7132
|
+
}, aria));
|
|
7133
|
+
case 'danger':
|
|
7134
|
+
// Struck through: aligned-looking glyph, cancelled. `currentColor` so the
|
|
7135
|
+
// glyph and the strike share the one colour declared on the wrapper.
|
|
7136
|
+
return /*#__PURE__*/React__default.createElement(StyledStruckThrough, null, /*#__PURE__*/React__default.createElement(Icon, _extends({
|
|
7137
|
+
icon: mdiFormatAlignRight,
|
|
7138
|
+
color: "currentColor",
|
|
7139
|
+
size: "small"
|
|
7140
|
+
}, aria)));
|
|
7141
|
+
default:
|
|
7142
|
+
return null;
|
|
7143
|
+
}
|
|
7144
|
+
}
|
|
7145
|
+
|
|
7146
|
+
/**
|
|
7147
|
+
* One status-list entry: its text, plus its own status icon and tooltip.
|
|
7148
|
+
*
|
|
7149
|
+
* A component rather than a plain render call because the tooltip text needs
|
|
7150
|
+
* translating, and `renderStructuredData` is a pure function called from the
|
|
7151
|
+
* adapter layer outside React — so it has no `t` to pass down. Resolving the key
|
|
7152
|
+
* here keeps the renderer's signature unchanged and matches how the rest of the
|
|
7153
|
+
* card resolves strings.
|
|
7154
|
+
*/
|
|
7155
|
+
const StatusListEntry = _ref => {
|
|
7156
|
+
let {
|
|
7157
|
+
item
|
|
7158
|
+
} = _ref;
|
|
7159
|
+
const {
|
|
7160
|
+
t
|
|
7161
|
+
} = useSignalCardTranslation('investigate');
|
|
7162
|
+
const tooltip = item.tooltipKey ? t(item.tooltipKey) : undefined;
|
|
7163
|
+
// The tooltip text doubles as the icon's accessible name — see renderItemStatusIcon.
|
|
7164
|
+
const icon = renderItemStatusIcon(item.status, item.matchMode, tooltip);
|
|
7165
|
+
return /*#__PURE__*/React__default.createElement("li", null, /*#__PURE__*/React__default.createElement(StyledStatusListText, null, item.text), icon ? /*#__PURE__*/React__default.createElement(StyledStatusListIcon, null, tooltip ? /*#__PURE__*/React__default.createElement(TooltipInPortalWrapper, {
|
|
7166
|
+
message: tooltip
|
|
7167
|
+
}, /*#__PURE__*/React__default.createElement("span", {
|
|
7168
|
+
style: {
|
|
7169
|
+
display: 'inline-flex'
|
|
7170
|
+
}
|
|
7171
|
+
}, icon)) : icon) : null);
|
|
7172
|
+
};
|
|
7173
|
+
|
|
7174
|
+
/**
|
|
7175
|
+
* Render a list whose entries each carry their own status.
|
|
7176
|
+
*
|
|
7177
|
+
* Used by the DMARC card's "DKIM Signing Domain" row, where one message can
|
|
7178
|
+
* carry several DKIM signatures whose alignment differs — an aligned domain gets a
|
|
7179
|
+
* green align glyph while a non-aligned one gets a red struck-through glyph, in the
|
|
7180
|
+
* same list. The row-level status is cleared by signal-logic whenever per-entry
|
|
7181
|
+
* statuses are present, so only these icons render and nothing double-counts.
|
|
7182
|
+
*/
|
|
7183
|
+
function renderStatusList(items) {
|
|
7184
|
+
if (!(items !== null && items !== void 0 && items.length)) {
|
|
7185
|
+
return null;
|
|
7186
|
+
}
|
|
7187
|
+
return /*#__PURE__*/React__default.createElement(StyledStatusList, null, items.map((item, i) => /*#__PURE__*/React__default.createElement(StatusListEntry, {
|
|
7188
|
+
key: i,
|
|
7189
|
+
item: item
|
|
7190
|
+
})));
|
|
7191
|
+
}
|
|
7192
|
+
|
|
6985
7193
|
// =============================================================================
|
|
6986
7194
|
// SIGNED HEADERS RENDERING
|
|
6987
7195
|
// =============================================================================
|
|
@@ -7180,6 +7388,13 @@ function renderStructuredData(data) {
|
|
|
7180
7388
|
}
|
|
7181
7389
|
case 'list':
|
|
7182
7390
|
return renderList(data.items);
|
|
7391
|
+
|
|
7392
|
+
// Additive: `list` above still ships. signal-logic falls back to it whenever
|
|
7393
|
+
// per-signature alignment cannot be computed (Domain Analyzer / `fromDns`
|
|
7394
|
+
// results, no `fromDomain`, or a domain sourced from the `dmarc.dkimDomain`
|
|
7395
|
+
// scalar rather than a signature), so both variants must keep working.
|
|
7396
|
+
case 'status-list':
|
|
7397
|
+
return renderStatusList(data.items);
|
|
7183
7398
|
case 'highlighted-text':
|
|
7184
7399
|
return /*#__PURE__*/React__default.createElement(WordHighlighter$1, {
|
|
7185
7400
|
searchWord: data.searchWord,
|