summaryrefslogtreecommitdiff
path: root/DomainDig/Models.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-07-17 10:39:17 -0500
committerChristian Cleberg <[email protected]>2026-07-17 10:57:30 -0500
commit6e273c2676ce29cef057d117e4427e031886e743 (patch)
treec16273cd4b3e022f2322cb9061b8e735542eea55 /DomainDig/Models.swift
parentd62480233819184db2e55741b05375818ebf3881 (diff)
downloaddomain-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.swift33
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)
}