diff options
| author | Christian Cleberg <[email protected]> | 2026-07-17 10:39:17 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-07-17 10:57:30 -0500 |
| commit | 6e273c2676ce29cef057d117e4427e031886e743 (patch) | |
| tree | c16273cd4b3e022f2322cb9061b8e735542eea55 /DomainDig/Models.swift | |
| parent | d62480233819184db2e55741b05375818ebf3881 (diff) | |
| download | domain-dig-6e273c2676ce29cef057d117e4427e031886e743.tar.gz domain-dig-6e273c2676ce29cef057d117e4427e031886e743.tar.bz2 domain-dig-6e273c2676ce29cef057d117e4427e031886e743.zip | |
v4.7.0: Add domain reputation/blocklist data source
- New DomainReputationResult model (status: clean/listed/unknown, listed
sources, checked-at) and a `reputation(domain:)` method on ExternalDataService,
mirroring the existing pluggable-URL enrichment pattern (ownership history,
DNS history, extended subdomains, pricing). With no endpoint configured
(the default; DomainDig ships no bundled third-party reputation dependency)
it resolves to unavailable rather than "clean".
- New .reputation FeatureCapability/DataCapability, gated Pro+ like domainPricing.
- Threaded reputation/reputationError through LookupSnapshot and HistoryEntry
(backward-compatible decode) so results persist with history entries.
- Auto-fetched in performLookup alongside pricing; surfaced as a "Reputation"
info row, folded into DomainInsightEngine's risk score/factors and top-level
insights (a listed domain raises risk score and adds a factor/insight), and
exported in text, CSV, and JSON report output.
- Reputation-driven risk changes ride the existing change-severity pipeline, so
a listed status flip is visible to monitoring the same way any other risk
delta is, without bespoke monitoring wiring.
Diffstat (limited to 'DomainDig/Models.swift')
| -rw-r--r-- | DomainDig/Models.swift | 33 |
1 files changed, 32 insertions, 1 deletions
diff --git a/DomainDig/Models.swift b/DomainDig/Models.swift index 376d264..d14ff4a 100644 --- a/DomainDig/Models.swift +++ b/DomainDig/Models.swift @@ -391,6 +391,7 @@ enum DataCapability: String, Codable { case dnsHistory case extendedSubdomains case domainPricing + case reputation } enum UsageCreditFeature: String, Codable, CaseIterable, Identifiable, Sendable { @@ -724,6 +725,29 @@ struct DomainPricingInsight: Codable, Equatable, Sendable { let collectedAt: Date } +enum DomainReputationStatus: String, Codable, Sendable { + case clean + case listed + case unknown + + var title: String { + switch self { + case .clean: + return "Clean" + case .listed: + return "Listed" + case .unknown: + return "Unknown" + } + } +} + +struct DomainReputationResult: Codable, Equatable, Sendable { + let status: DomainReputationStatus + let listedSources: [String] + let checkedAt: Date +} + enum HistoryDateFilter: String, CaseIterable, Identifiable { case today case last7Days @@ -2285,6 +2309,7 @@ struct HistoryEntry: Identifiable, Codable { var extendedSubdomains: [DiscoveredSubdomain] var dnsHistory: [DNSHistoryEvent] var domainPricing: DomainPricingInsight? + var reputation: DomainReputationResult? var portScanResults: [PortScanResult] var hstsPreloaded: Bool? var availabilityResult: DomainAvailabilityResult? @@ -2327,6 +2352,7 @@ struct HistoryEntry: Identifiable, Codable { var extendedSubdomainsError: String? var dnsHistoryError: String? var domainPricingError: String? + var reputationError: String? var portScanError: String? init(domain: String, timestamp: Date, trackedDomainID: UUID? = nil, note: String? = nil, dnsSections: [DNSSection], @@ -2344,6 +2370,7 @@ struct HistoryEntry: Identifiable, Codable { ptrRecord: String? = nil, redirectChain: [RedirectHop] = [], subdomains: [DiscoveredSubdomain] = [], extendedSubdomains: [DiscoveredSubdomain] = [], dnsHistory: [DNSHistoryEvent] = [], domainPricing: DomainPricingInsight? = nil, + reputation: DomainReputationResult? = nil, portScanResults: [PortScanResult] = [], hstsPreloaded: Bool? = nil, availabilityResult: DomainAvailabilityResult? = nil, suggestions: [DomainSuggestionResult] = [], appVersion: String = "2.7.0", @@ -2362,7 +2389,7 @@ struct HistoryEntry: Identifiable, Codable { emailSecurityError: String? = nil, ownershipError: String? = nil, ownershipHistoryError: String? = nil, ptrError: String? = nil, redirectChainError: String? = nil, subdomainsError: String? = nil, extendedSubdomainsError: String? = nil, dnsHistoryError: String? = nil, - domainPricingError: String? = nil, portScanError: String? = nil) { + domainPricingError: String? = nil, reputationError: String? = nil, portScanError: String? = nil) { self.domain = domain self.timestamp = timestamp self.trackedDomainID = trackedDomainID @@ -2390,6 +2417,7 @@ struct HistoryEntry: Identifiable, Codable { self.extendedSubdomains = extendedSubdomains self.dnsHistory = dnsHistory self.domainPricing = domainPricing + self.reputation = reputation self.portScanResults = portScanResults self.hstsPreloaded = hstsPreloaded self.availabilityResult = availabilityResult @@ -2432,6 +2460,7 @@ struct HistoryEntry: Identifiable, Codable { self.extendedSubdomainsError = extendedSubdomainsError self.dnsHistoryError = dnsHistoryError self.domainPricingError = domainPricingError + self.reputationError = reputationError self.portScanError = portScanError } @@ -2465,6 +2494,7 @@ struct HistoryEntry: Identifiable, Codable { extendedSubdomains = try container.decodeIfPresent([DiscoveredSubdomain].self, forKey: .extendedSubdomains) ?? [] dnsHistory = try container.decodeIfPresent([DNSHistoryEvent].self, forKey: .dnsHistory) ?? [] domainPricing = try container.decodeIfPresent(DomainPricingInsight.self, forKey: .domainPricing) + reputation = try container.decodeIfPresent(DomainReputationResult.self, forKey: .reputation) portScanResults = try container.decodeIfPresent([PortScanResult].self, forKey: .portScanResults) ?? [] hstsPreloaded = try container.decodeIfPresent(Bool.self, forKey: .hstsPreloaded) availabilityResult = try container.decodeIfPresent(DomainAvailabilityResult.self, forKey: .availabilityResult) @@ -2510,6 +2540,7 @@ struct HistoryEntry: Identifiable, Codable { extendedSubdomainsError = try container.decodeIfPresent(String.self, forKey: .extendedSubdomainsError) dnsHistoryError = try container.decodeIfPresent(String.self, forKey: .dnsHistoryError) domainPricingError = try container.decodeIfPresent(String.self, forKey: .domainPricingError) + reputationError = try container.decodeIfPresent(String.self, forKey: .reputationError) portScanError = try container.decodeIfPresent(String.self, forKey: .portScanError) } |
