Commit 6e273c2676

6e273c2676ce29cef057d117e4427e031886e743

parent: d624802338

Unsigned

cmc <hello@cleberg.net> · 2026-07-17 15:39 UTC

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.

Layout: unified · split

DomainDig/DataAccessService.swift +2
@@ -11,6 +11,8 @@ enum DataAccessService {
1111 return FeatureAccessService.hasAccess(to: .extendedSubdomains)
1212 case .domainPricing:
1313 return FeatureAccessService.hasAccess(to: .domainPricing)
14 case .reputation:
15 return FeatureAccessService.hasAccess(to: .reputation)
1416 }
1517 }
1618}
DomainDig/DomainInsightEngine.swift +16
@@ -303,6 +303,19 @@ enum DomainInsightEngine {
303303 }
304304 }
305305
306 if let reputation = snapshot.reputation {
307 switch reputation.status {
308 case .listed:
309 score += 25
310 let sourceList = reputation.listedSources.isEmpty ? "" : " (\(reputation.listedSources.joined(separator: ", ")))"
311 factors.append(.init(description: "Domain is flagged by a configured reputation source\(sourceList)", impact: .negative))
312 case .clean:
313 factors.append(.init(description: "Domain is clean against the configured reputation source", impact: .positive))
314 case .unknown:
315 break
316 }
317 }
318
306319 let clampedScore = min(max(score, 0), 100)
307320 let level: RiskLevel
308321 switch clampedScore {
@@ -327,6 +340,9 @@ enum DomainInsightEngine {
327340 ) -> [String] {
328341 var items: [String] = []
329342
343 if snapshot.reputation?.status == .listed {
344 items.append("Domain is flagged by a configured reputation source")
345 }
330346 if let group = subdomainGroups.first(where: { $0.label == "staging" || $0.label == "dev" }) {
331347 items.append("Multiple \(group.label) subdomains suggest non-production environments are exposed")
332348 }
DomainDig/DomainMonitoringService.swift +4
@@ -516,6 +516,7 @@ final class DomainMonitoringService {
516516 extendedSubdomains: snapshot.extendedSubdomains,
517517 dnsHistory: snapshot.dnsHistory,
518518 domainPricing: snapshot.domainPricing,
519 reputation: snapshot.reputation,
519520 portScanResults: snapshot.portScanResults,
520521 hstsPreloaded: snapshot.hstsPreloaded,
521522 availabilityResult: snapshot.availabilityResult,
@@ -554,6 +555,7 @@ final class DomainMonitoringService {
554555 extendedSubdomainsError: snapshot.extendedSubdomainsError,
555556 dnsHistoryError: snapshot.dnsHistoryError,
556557 domainPricingError: snapshot.domainPricingError,
558 reputationError: snapshot.reputationError,
557559 portScanError: snapshot.portScanError
558560 )
559561
@@ -930,6 +932,8 @@ final class DomainMonitoringService {
930932 dnsHistoryError: previousSnapshot.dnsHistoryError,
931933 domainPricing: previousSnapshot.domainPricing,
932934 domainPricingError: previousSnapshot.domainPricingError,
935 reputation: previousSnapshot.reputation,
936 reputationError: previousSnapshot.reputationError,
933937 portScanResults: previousSnapshot.portScanResults,
934938 portScanError: previousSnapshot.portScanError,
935939 changeSummary: previousSnapshot.changeSummary,
DomainDig/DomainViewModel.swift +51
@@ -232,6 +232,9 @@ final class DomainViewModel {
232232 var domainPricing: DomainPricingInsight?
233233 var domainPricingLoading = false
234234 var domainPricingError: String?
235 var reputation: DomainReputationResult?
236 var reputationLoading = false
237 var reputationError: String?
235238 var usageCredits: [UsageCreditFeature: UsageCreditStatus] = DomainViewModel.defaultUsageCredits()
236239
237240 var portScanResults: [PortScanResult] = []
@@ -642,6 +645,8 @@ final class DomainViewModel {
642645 dnsHistoryError: dnsHistoryError,
643646 domainPricing: domainPricing,
644647 domainPricingError: domainPricingError,
648 reputation: reputation,
649 reputationError: reputationError,
645650 portScanResults: allPortScanResults,
646651 portScanError: combinedPortScanError,
647652 changeSummary: currentChangeSummary,
@@ -1883,6 +1888,11 @@ final class DomainViewModel {
18831888 await refreshDomainPricing(for: snapshot.domain, persistAfterFetch: false)
18841889 }
18851890
1891 if DataAccessService.hasAccess(to: .reputation), reputation == nil {
1892 DomainDebugLog.debug("DomainViewModel.performLookup loadingReputation domain=\(domain)")
1893 await refreshReputation(for: snapshot.domain, persistAfterFetch: false)
1894 }
1895
18861896 guard snapshot.statusMessage == nil else {
18871897 return history.first(where: { $0.id == snapshot.historyEntryID })
18881898 }
@@ -2053,6 +2063,8 @@ final class DomainViewModel {
20532063 dnsHistoryError: previousSnapshot.dnsHistoryError,
20542064 domainPricing: previousSnapshot.domainPricing,
20552065 domainPricingError: previousSnapshot.domainPricingError,
2066 reputation: previousSnapshot.reputation,
2067 reputationError: previousSnapshot.reputationError,
20562068 portScanResults: previousSnapshot.portScanResults,
20572069 portScanError: previousSnapshot.portScanError,
20582070 changeSummary: previousSnapshot.changeSummary,
@@ -2474,6 +2486,7 @@ final class DomainViewModel {
24742486 extendedSubdomains: snapshot.extendedSubdomains,
24752487 dnsHistory: snapshot.dnsHistory,
24762488 domainPricing: snapshot.domainPricing,
2489 reputation: snapshot.reputation,
24772490 portScanResults: snapshot.portScanResults,
24782491 hstsPreloaded: snapshot.hstsPreloaded,
24792492 availabilityResult: snapshot.availabilityResult,
@@ -2516,6 +2529,7 @@ final class DomainViewModel {
25162529 extendedSubdomainsError: snapshot.extendedSubdomainsError,
25172530 dnsHistoryError: snapshot.dnsHistoryError,
25182531 domainPricingError: snapshot.domainPricingError,
2532 reputationError: snapshot.reputationError,
25192533 portScanError: snapshot.portScanError
25202534 )
25212535
@@ -3844,6 +3858,8 @@ final class DomainViewModel {
38443858 dnsHistoryError: nil,
38453859 domainPricing: nil,
38463860 domainPricingError: nil,
3861 reputation: nil,
3862 reputationError: nil,
38473863 portScanResults: [],
38483864 portScanError: nil,
38493865 changeSummary: trackedDomain.lastChangeSummary,
@@ -4005,6 +4021,18 @@ final class DomainViewModel {
40054021 rows.append(InfoRowViewData(label: "Auction", value: auctionSignal, tone: .secondary))
40064022 }
40074023 }
4024 if let reputation = snapshot.reputation {
4025 let tone: ResultTone
4026 switch reputation.status {
4027 case .clean: tone = .success
4028 case .listed: tone = .failure
4029 case .unknown: tone = .secondary
4030 }
4031 let value = reputation.status == .listed && !reputation.listedSources.isEmpty
4032 ? "\(reputation.status.title) (\(reputation.listedSources.joined(separator: ", ")))"
4033 : reputation.status.title
4034 rows.append(InfoRowViewData(label: "Reputation", value: value, tone: tone))
4035 }
40084036 if let certificateStatus = certificateBadgeLabel(from: snapshot) {
40094037 rows.insert(
40104038 InfoRowViewData(
@@ -4700,6 +4728,29 @@ final class DomainViewModel {
47004728 }
47014729 }
47024730
4731 private func refreshReputation(for domain: String, persistAfterFetch: Bool) async {
4732 reputationLoading = true
4733 let outcome = await ExternalDataService.shared.reputation(domain: domain)
4734
4735 switch outcome.value {
4736 case let .success(result):
4737 reputation = result
4738 reputationError = nil
4739 case let .empty(message):
4740 reputation = nil
4741 reputationError = conciseExternalMessage(message, fallback: "Reputation check unavailable")
4742 case let .error(message):
4743 reputation = nil
4744 reputationError = conciseExternalMessage(message, fallback: "Reputation check unavailable")
4745 }
4746
4747 reputationLoading = false
4748
4749 if persistAfterFetch {
4750 _ = saveHistoryEntry(replaceLatest: true)
4751 }
4752 }
4753
47034754 private func conciseExternalMessage(_ message: String, fallback: String) -> String {
47044755 let trimmed = message.trimmingCharacters(in: .whitespacesAndNewlines)
47054756 if trimmed.isEmpty {
DomainDig/ExternalDataService.swift +66 −1
@@ -8,12 +8,14 @@ actor ExternalDataService {
88 case dnsHistory(String)
99 case extendedSubdomains(String)
1010 case pricing(String)
11 case reputation(String)
1112 }
1213
1314 private enum RateLimitBucket: Hashable {
1415 case history
1516 case subdomains
1617 case pricing
18 case reputation
1719
1820 var minimumSpacing: TimeInterval {
1921 switch self {
@@ -23,6 +25,8 @@ actor ExternalDataService {
2325 return 1.5
2426 case .pricing:
2527 return 1.0
28 case .reputation:
29 return 1.0
2630 }
2731 }
2832 }
@@ -32,6 +36,7 @@ actor ExternalDataService {
3236 case dnsHistory(ServiceResult<[DNSHistoryEvent]>)
3337 case extendedSubdomains(ServiceResult<[DiscoveredSubdomain]>)
3438 case pricing(ServiceResult<DomainPricingInsight>)
39 case reputation(ServiceResult<DomainReputationResult>)
3540 }
3641
3742 private struct CacheEntry {
@@ -44,6 +49,7 @@ actor ExternalDataService {
4449 let dnsHistoryURL: String?
4550 let extendedSubdomainsURL: String?
4651 let pricingURL: String?
52 let reputationURL: String?
4753 }
4854
4955 private let ttl: TimeInterval = 900
@@ -161,6 +167,25 @@ actor ExternalDataService {
161167 )
162168 }
163169
170 /// Checks a domain against a configured blocklist/reputation endpoint. With
171 /// no endpoint configured (the default, since DomainDig ships no bundled
172 /// third-party reputation dependency) this resolves to `.empty` and callers
173 /// treat the result as unavailable rather than "clean".
174 func reputation(domain: String) async -> CachedLookupResult<ServiceResult<DomainReputationResult>> {
175 let normalizedDomain = Self.normalize(domain)
176 return await execute(
177 key: .reputation(normalizedDomain),
178 rateLimitBucket: .reputation,
179 extract: { payload in
180 guard case let .reputation(result) = payload else { return nil }
181 return result
182 },
183 operation: { [configuration = configuration()] in
184 .reputation(await self.fetchReputation(domain: normalizedDomain, configuration: configuration))
185 }
186 )
187 }
188
164189 private func execute<T>(
165190 key: RequestKey,
166191 rateLimitBucket: RateLimitBucket,
@@ -213,7 +238,9 @@ actor ExternalDataService {
213238 extendedSubdomainsURL: defaults.string(forKey: "externalData.extendedSubdomainsURL")
214239 ?? Bundle.main.object(forInfoDictionaryKey: "ExternalExtendedSubdomainsURL") as? String,
215240 pricingURL: defaults.string(forKey: "externalData.pricingURL")
216 ?? Bundle.main.object(forInfoDictionaryKey: "ExternalPricingURL") as? String
241 ?? Bundle.main.object(forInfoDictionaryKey: "ExternalPricingURL") as? String,
242 reputationURL: defaults.string(forKey: "externalData.reputationURL")
243 ?? Bundle.main.object(forInfoDictionaryKey: "ExternalReputationURL") as? String
217244 )
218245 }
219246
@@ -290,6 +317,28 @@ actor ExternalDataService {
290317 }
291318 }
292319
320 private func fetchReputation(
321 domain: String,
322 configuration: Configuration
323 ) async -> ServiceResult<DomainReputationResult> {
324 guard let template = configuration.reputationURL,
325 let url = Self.url(from: template, domain: domain) else {
326 return .empty("No reputation source configured")
327 }
328
329 switch await requestData(url: url) {
330 case let .success(data):
331 guard let reputation = parseReputation(from: data) else {
332 return .error("Invalid external response")
333 }
334 return .success(reputation)
335 case let .empty(message):
336 return .empty(message)
337 case let .error(message):
338 return .error(message)
339 }
340 }
341
293342 private func requestData(url: URL) async -> ServiceResult<Data> {
294343 for attempt in 0..<2 {
295344 do {
@@ -403,6 +452,22 @@ actor ExternalDataService {
403452 )
404453 }
405454
455 private func parseReputation(from data: Data) -> DomainReputationResult? {
456 guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
457 return nil
458 }
459
460 let listedSources = json["listed_sources"] as? [String] ?? []
461 let status: DomainReputationStatus
462 if let statusString = json["status"] as? String, let parsed = DomainReputationStatus(rawValue: statusString) {
463 status = parsed
464 } else {
465 status = listedSources.isEmpty ? .clean : .listed
466 }
467
468 return DomainReputationResult(status: status, listedSources: listedSources, checkedAt: Date())
469 }
470
406471 private static func localOwnershipHistory(
407472 domain: String,
408473 currentOwnership: DomainOwnership?,
DomainDig/FeatureAccessService.swift +5 −2
@@ -32,6 +32,7 @@ enum FeatureCapability: String, CaseIterable, Identifiable {
3232 case dnsHistory
3333 case extendedSubdomains
3434 case domainPricing
35 case reputation
3536
3637 var id: String { rawValue }
3738
@@ -61,6 +62,8 @@ enum FeatureCapability: String, CaseIterable, Identifiable {
6162 return "Extended subdomains"
6263 case .domainPricing:
6364 return "Domain pricing"
65 case .reputation:
66 return "Domain reputation"
6467 }
6568 }
6669}
@@ -157,7 +160,7 @@ enum FeatureAccessService {
157160 switch capability {
158161 case .workflows, .batchOperations, .automatedMonitoring, .localAlerts, .advancedExports:
159162 return "Available in Pro"
160 case .ownershipHistory, .dnsHistory, .extendedSubdomains, .domainPricing:
163 case .ownershipHistory, .dnsHistory, .extendedSubdomains, .domainPricing, .reputation:
161164 return "Available in Pro+"
162165 case .limitedTracking:
163166 return "Tracking is limited on Free"
@@ -219,7 +222,7 @@ enum FeatureAccessService {
219222 static func upgradePrompt(for capability: FeatureCapability) -> UpgradePromptContext {
220223 let title: String
221224 switch capability {
222 case .ownershipHistory, .dnsHistory, .extendedSubdomains, .domainPricing:
225 case .ownershipHistory, .dnsHistory, .extendedSubdomains, .domainPricing, .reputation:
223226 title = "Available in Pro+"
224227 default:
225228 title = "Available in Pro"
DomainDig/Models.swift +32 −1
@@ -391,6 +391,7 @@ enum DataCapability: String, Codable {
391391 case dnsHistory
392392 case extendedSubdomains
393393 case domainPricing
394 case reputation
394395}
395396
396397enum UsageCreditFeature: String, Codable, CaseIterable, Identifiable, Sendable {
@@ -724,6 +725,29 @@ struct DomainPricingInsight: Codable, Equatable, Sendable {
724725 let collectedAt: Date
725726}
726727
728enum DomainReputationStatus: String, Codable, Sendable {
729 case clean
730 case listed
731 case unknown
732
733 var title: String {
734 switch self {
735 case .clean:
736 return "Clean"
737 case .listed:
738 return "Listed"
739 case .unknown:
740 return "Unknown"
741 }
742 }
743}
744
745struct DomainReputationResult: Codable, Equatable, Sendable {
746 let status: DomainReputationStatus
747 let listedSources: [String]
748 let checkedAt: Date
749}
750
727751enum HistoryDateFilter: String, CaseIterable, Identifiable {
728752 case today
729753 case last7Days
@@ -2285,6 +2309,7 @@ struct HistoryEntry: Identifiable, Codable {
22852309 var extendedSubdomains: [DiscoveredSubdomain]
22862310 var dnsHistory: [DNSHistoryEvent]
22872311 var domainPricing: DomainPricingInsight?
2312 var reputation: DomainReputationResult?
22882313 var portScanResults: [PortScanResult]
22892314 var hstsPreloaded: Bool?
22902315 var availabilityResult: DomainAvailabilityResult?
@@ -2327,6 +2352,7 @@ struct HistoryEntry: Identifiable, Codable {
23272352 var extendedSubdomainsError: String?
23282353 var dnsHistoryError: String?
23292354 var domainPricingError: String?
2355 var reputationError: String?
23302356 var portScanError: String?
23312357
23322358 init(domain: String, timestamp: Date, trackedDomainID: UUID? = nil, note: String? = nil, dnsSections: [DNSSection],
@@ -2344,6 +2370,7 @@ struct HistoryEntry: Identifiable, Codable {
23442370 ptrRecord: String? = nil, redirectChain: [RedirectHop] = [], subdomains: [DiscoveredSubdomain] = [],
23452371 extendedSubdomains: [DiscoveredSubdomain] = [], dnsHistory: [DNSHistoryEvent] = [],
23462372 domainPricing: DomainPricingInsight? = nil,
2373 reputation: DomainReputationResult? = nil,
23472374 portScanResults: [PortScanResult] = [],
23482375 hstsPreloaded: Bool? = nil, availabilityResult: DomainAvailabilityResult? = nil,
23492376 suggestions: [DomainSuggestionResult] = [], appVersion: String = "2.7.0",
@@ -2362,7 +2389,7 @@ struct HistoryEntry: Identifiable, Codable {
23622389 emailSecurityError: String? = nil, ownershipError: String? = nil, ownershipHistoryError: String? = nil,
23632390 ptrError: String? = nil, redirectChainError: String? = nil, subdomainsError: String? = nil,
23642391 extendedSubdomainsError: String? = nil, dnsHistoryError: String? = nil,
2365 domainPricingError: String? = nil, portScanError: String? = nil) {
2392 domainPricingError: String? = nil, reputationError: String? = nil, portScanError: String? = nil) {
23662393 self.domain = domain
23672394 self.timestamp = timestamp
23682395 self.trackedDomainID = trackedDomainID
@@ -2390,6 +2417,7 @@ struct HistoryEntry: Identifiable, Codable {
23902417 self.extendedSubdomains = extendedSubdomains
23912418 self.dnsHistory = dnsHistory
23922419 self.domainPricing = domainPricing
2420 self.reputation = reputation
23932421 self.portScanResults = portScanResults
23942422 self.hstsPreloaded = hstsPreloaded
23952423 self.availabilityResult = availabilityResult
@@ -2432,6 +2460,7 @@ struct HistoryEntry: Identifiable, Codable {
24322460 self.extendedSubdomainsError = extendedSubdomainsError
24332461 self.dnsHistoryError = dnsHistoryError
24342462 self.domainPricingError = domainPricingError
2463 self.reputationError = reputationError
24352464 self.portScanError = portScanError
24362465 }
24372466
@@ -2465,6 +2494,7 @@ struct HistoryEntry: Identifiable, Codable {
24652494 extendedSubdomains = try container.decodeIfPresent([DiscoveredSubdomain].self, forKey: .extendedSubdomains) ?? []
24662495 dnsHistory = try container.decodeIfPresent([DNSHistoryEvent].self, forKey: .dnsHistory) ?? []
24672496 domainPricing = try container.decodeIfPresent(DomainPricingInsight.self, forKey: .domainPricing)
2497 reputation = try container.decodeIfPresent(DomainReputationResult.self, forKey: .reputation)
24682498 portScanResults = try container.decodeIfPresent([PortScanResult].self, forKey: .portScanResults) ?? []
24692499 hstsPreloaded = try container.decodeIfPresent(Bool.self, forKey: .hstsPreloaded)
24702500 availabilityResult = try container.decodeIfPresent(DomainAvailabilityResult.self, forKey: .availabilityResult)
@@ -2510,6 +2540,7 @@ struct HistoryEntry: Identifiable, Codable {
25102540 extendedSubdomainsError = try container.decodeIfPresent(String.self, forKey: .extendedSubdomainsError)
25112541 dnsHistoryError = try container.decodeIfPresent(String.self, forKey: .dnsHistoryError)
25122542 domainPricingError = try container.decodeIfPresent(String.self, forKey: .domainPricingError)
2543 reputationError = try container.decodeIfPresent(String.self, forKey: .reputationError)
25132544 portScanError = try container.decodeIfPresent(String.self, forKey: .portScanError)
25142545 }
25152546
DomainInspectionService.swift +2
@@ -369,6 +369,8 @@ struct DomainInspectionService {
369369 dnsHistoryError: nil,
370370 domainPricing: nil,
371371 domainPricingError: nil,
372 reputation: nil,
373 reputationError: nil,
372374 portScanResults: portScanResults.value,
373375 portScanError: portScanResults.message,
374376 changeSummary: nil,
DomainReportBuilder.swift +2
@@ -38,6 +38,7 @@ struct DomainReport: Codable {
3838 let extendedSubdomains: [String]
3939 let dnsHistory: [DNSHistoryEvent]
4040 let domainPricing: DomainPricingInsight?
41 let reputation: DomainReputationResult?
4142 let subdomainGroups: [SubdomainGroup]
4243 let riskAssessment: DomainRiskAssessment
4344 let insights: [String]
@@ -360,6 +361,7 @@ struct DomainReportBuilder {
360361 extendedSubdomains: snapshot.extendedSubdomains.map(\.hostname),
361362 dnsHistory: snapshot.dnsHistory,
362363 domainPricing: snapshot.domainPricing,
364 reputation: snapshot.reputation,
363365 subdomainGroups: analysis.subdomainGroups,
364366 riskAssessment: analysis.riskAssessment,
365367 insights: analysis.insights,
DomainReportExporter.swift +8 −1
@@ -69,7 +69,8 @@ enum DomainReportExporter {
6969 "Email: \(report.email.summary)",
7070 "Subdomains: \(report.subdomains.count)",
7171 "Extended Subdomains: \(report.extendedSubdomains.count)",
72 "External Price: \(report.domainPricing?.estimatedPrice ?? "Unavailable")"
72 "External Price: \(report.domainPricing?.estimatedPrice ?? "Unavailable")",
73 "Reputation: \(report.reputation?.status.title ?? "Unavailable")"
7374 ]
7475 }
7576
@@ -406,6 +407,8 @@ enum DomainReportExporter {
406407 "pricing_resale_signal",
407408 "pricing_auction_signal",
408409 "pricing_source",
410 "reputation_status",
411 "reputation_listed_sources",
409412 "open_ports",
410413 "reachability_summary",
411414 "geolocation_summary",
@@ -438,6 +441,8 @@ enum DomainReportExporter {
438441 let subdomainGroups = report.subdomainGroups.map { "\($0.label):\($0.subdomains.count)" }.joined(separator: " | ")
439442 let ownershipHistory = report.ownershipHistory.map { "\($0.date.ISO8601Format()) \($0.summary)" }.joined(separator: " | ")
440443 let dnsHistory = report.dnsHistory.map { "\($0.date.ISO8601Format()) \($0.summary)" }.joined(separator: " | ")
444 let reputationStatus = report.reputation?.status.rawValue ?? ""
445 let reputationListedSources = report.reputation?.listedSources.joined(separator: " | ") ?? ""
441446
442447 return [
443448 report.domain,
@@ -483,6 +488,8 @@ enum DomainReportExporter {
483488 report.domainPricing?.resaleSignal ?? "",
484489 report.domainPricing?.auctionSignal ?? "",
485490 report.domainPricing?.source ?? "",
491 reputationStatus,
492 reputationListedSources,
486493 openPorts,
487494 report.network.reachabilitySummary,
488495 report.network.geolocationSummary,
LookupSnapshot.swift +4
@@ -68,6 +68,8 @@ struct LookupSnapshot {
6868 let dnsHistoryError: String?
6969 let domainPricing: DomainPricingInsight?
7070 let domainPricingError: String?
71 let reputation: DomainReputationResult?
72 let reputationError: String?
7173 let portScanResults: [PortScanResult]
7274 let portScanError: String?
7375 let changeSummary: DomainChangeSummary?
@@ -150,6 +152,8 @@ extension HistoryEntry {
150152 dnsHistoryError: dnsHistoryError,
151153 domainPricing: domainPricing,
152154 domainPricingError: domainPricingError,
155 reputation: reputation,
156 reputationError: reputationError,
153157 portScanResults: portScanResults,
154158 portScanError: portScanError,
155159 changeSummary: changeSummary,