whois-parser-next 0.3.0 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c90a3f9e48b3ede88480bcaec9eabccfe31a7f60cf867df226192f54e0dbe29
4
- data.tar.gz: 683529a7de7ea08c12065878dbc59d364f43163264d2c8338f1b90011aec5821
3
+ metadata.gz: 1ac79159293403464f7e386fd283634642e6eda102d7e486e105b399089e34cf
4
+ data.tar.gz: 0fb55415df4c88415bac870059b30f25fbf5f488526241b1358ce15257b8bcb9
5
5
  SHA512:
6
- metadata.gz: d9608362d4824d5c3a9044b3dd59e2c82935e915c88b938af75c9f8904852fab632b2e76132f25fd7a4faf7652ed63cdd54f86327c73e9efa1326df5d0225c14
7
- data.tar.gz: 8c93323b08ea3c950f809bd201026a4a2a5086f9d5fddfcb31dad8a2888a5857ad4810f3932b3995b1abf4adee44fa4bf6354f96191527b5799952f4508a40e2
6
+ metadata.gz: 58999ed26705e0ee3ff4e78428b70ee0f20f45e184693debdee4a899617274662ba2f1074f8ca0bb6109ac7c6c702a81ed88ee32138a29e68dd7a1391cd13ffa
7
+ data.tar.gz: c537956618e781da069518bc6428e39f7953368f001c833582b349f19c3d4d6a7cc7a73e1e85f93d3973c978083497c2fc424c616ad660dcd4ffaf5d8669be92
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  This project uses [Semantic Versioning 2.0.0](http://semver.org/).
4
4
 
5
+ #### Release 0.3.1 (2026-09-25)
6
+
7
+ - ADDED: Added paired registered and exact authoritative-absence parsing for
8
+ `.ma` and `.hn`. Empty and denied replies remain unavailable, rate limits
9
+ remain throttled, and conflicting or incomplete replies remain unknown.
10
+ - FIXED: Corrected `.pk` parsing for PKNIC's observed indented response
11
+ fields. Registration and availability still require explicit markers;
12
+ incomplete or conflicting replies remain unknown, and denied or throttled
13
+ replies remain non-positive.
14
+ - EVIDENCE: [Current `.ma` and `.hn` responses](docs/audits/top-126-150-tlds-2026-09-19.md)
15
+ and [production-shaped `.pk` responses](docs/audits/top1000-es-pk-shop-dev-app-2026-09-25.md).
16
+
5
17
 
6
18
  #### Release 0.3.0 (2026-09-25)
7
19
 
data/README.md CHANGED
@@ -38,7 +38,7 @@ of current WHOIS responses, explicit unknown states, registry-specific
38
38
  fixtures, and safe handling of malformed, rate-limited, retired, or changed
39
39
  server responses.
40
40
 
41
- The current source version is `0.3.0`. Check the [RubyGems project
41
+ The current source version is `0.3.1`. Check the [RubyGems project
42
42
  page](https://rubygems.org/gems/whois-parser-next) for its publication status
43
43
  and the latest published version.
44
44
 
@@ -54,11 +54,10 @@ Whois::Parser
54
54
  ```
55
55
 
56
56
  Existing applications can retain `require "whois-parser"` and their
57
- `Whois::Parser` integrations. To install version 0.3.0 after it appears on
58
- RubyGems, pin it as follows:
57
+ `Whois::Parser` integrations. To install version 0.3.1, pin it as follows:
59
58
 
60
59
  ```ruby
61
- gem "whois-parser-next", "= 0.3.0", require: "whois-parser"
60
+ gem "whois-parser-next", "= 0.3.1", require: "whois-parser"
62
61
  ```
63
62
 
64
63
  ## Requirements
@@ -1,6 +1,6 @@
1
1
  module Whois
2
2
  class Parser
3
3
  # The current library version.
4
- VERSION = "0.3.0".freeze
4
+ VERSION = "0.3.1".freeze
5
5
  end
6
6
  end
@@ -24,14 +24,14 @@ module Whois
24
24
 
25
25
  property_supported :status do
26
26
  if no_object_found?
27
- :available
27
+ registration_fields_present? ? :unknown : :available
28
28
  else
29
29
  super()
30
30
  end
31
31
  end
32
32
 
33
33
  property_supported :available? do
34
- no_object_found? || super()
34
+ status == :available
35
35
  end
36
36
 
37
37
  property_supported :registered? do
@@ -43,6 +43,12 @@ module Whois
43
43
  def no_object_found?
44
44
  content_for_scanner.match?(/^The queried object does not exist:\s*No Object Found\s*$/i)
45
45
  end
46
+
47
+ def registration_fields_present?
48
+ content_for_scanner.match?(
49
+ /^[ \t]*(?:Domain Status|Domain ID|Creation Date|Updated Date|Registry Expiry Date|Name Server|(?:Sponsoring )?Registrar|Registrant|Admin|Billing|Tech):/i
50
+ )
51
+ end
46
52
  end
47
53
 
48
54
  end
@@ -52,17 +52,17 @@ module Whois
52
52
  private
53
53
 
54
54
  def echoed_domain
55
- content_for_scanner[/^Domain:\s*((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+pk)\s*$/i, 1]
55
+ content_for_scanner[/^ {0,8}Domain:[ \t]*((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+pk)[ \t]*$/i, 1]
56
56
  end
57
57
 
58
58
  def registered_evidence?
59
- !!(echoed_domain && content_for_scanner.match?(/^Status: Domain is Registered\s*$/))
59
+ !!(echoed_domain && content_for_scanner.match?(/^ {0,8}Status: Domain is Registered[ \t]*$/))
60
60
  end
61
61
 
62
62
  def available_evidence?
63
63
  !!(echoed_domain &&
64
- content_for_scanner.match?(/^Status: Not Registered, and may be available if valid\s*$/) &&
65
- content_for_scanner.match?(/^Available: Yes\.\s*$/))
64
+ content_for_scanner.match?(/^ {0,8}Status: Not Registered, and may be available if valid[ \t]*$/) &&
65
+ content_for_scanner.match?(/^ {0,8}Available: Yes\.[ \t]*$/))
66
66
  end
67
67
  end
68
68
 
@@ -27,21 +27,15 @@ module Whois
27
27
  include RegistryResponseSafety
28
28
 
29
29
  property_supported :status do
30
- if available?
31
- :available
32
- elsif content_for_scanner.match?(/^domain:/i)
33
- :registered
34
- else
35
- :unknown
36
- end
30
+ classify_status
37
31
  end
38
32
 
39
33
  property_supported :available? do
40
- !!(content_for_scanner =~ /^%error 230 No Objects Found/)
34
+ classify_status == :available
41
35
  end
42
36
 
43
37
  property_supported :registered? do
44
- status == :registered
38
+ classify_status == :registered
45
39
  end
46
40
 
47
41
 
@@ -59,6 +53,92 @@ module Whois
59
53
 
60
54
  property_not_supported :expires_on
61
55
 
56
+ private
57
+
58
+ def classify_status
59
+ return :unknown if conflicting_evidence?
60
+
61
+ return :registered if registered_evidence?
62
+ return :available if available_evidence?
63
+
64
+ :unknown
65
+ end
66
+
67
+ def conflicting_evidence?
68
+ return false unless absence_markers.any?
69
+
70
+ record_details_present? || legacy_record_evidence?
71
+ end
72
+
73
+ def registered_evidence?
74
+ return false unless valid_domain_name_fields? && domain_names.one? && absence_markers.empty?
75
+
76
+ current_record_evidence? || legacy_record_evidence?
77
+ end
78
+
79
+ def current_record_evidence?
80
+ domain_status_values == ['clientTransferProhibited https://icann.org/epp#clientTransferProhibited']
81
+ end
82
+
83
+ def legacy_record_evidence?
84
+ legacy_domain_names.one? &&
85
+ content_for_scanner.match?(/^[ \t]*domain:Class-Name:domain[ \t]*$/i) &&
86
+ content_for_scanner.match?(/^[ \t]*%ok[ \t]*$/i)
87
+ end
88
+
89
+ def available_evidence?
90
+ absence_markers.one? && valid_domain_name_fields? && !record_details_present? &&
91
+ !legacy_record_evidence?
92
+ end
93
+
94
+ def valid_domain_name_fields?
95
+ fields = domain_name_field_values
96
+ return false if domain_name_field? && fields.empty?
97
+
98
+ fields.empty? || (fields.one? && domain_names.one?)
99
+ end
100
+
101
+ def domain_names
102
+ (current_domain_names + legacy_domain_names).uniq
103
+ end
104
+
105
+ def domain_name_field_values
106
+ content_for_scanner.scan(/^[ \t]*Domain Name:[ \t]*(\S+)[ \t]*$/i).flatten +
107
+ content_for_scanner.scan(/^[ \t]*domain:Domain-Name:[ \t]*(\S+)[ \t]*$/i).flatten
108
+ end
109
+
110
+ def current_domain_names
111
+ content_for_scanner.scan(
112
+ /^[ \t]*Domain Name:[ \t]*((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+ma)[ \t]*$/i
113
+ ).flatten.map(&:downcase)
114
+ end
115
+
116
+ def legacy_domain_names
117
+ content_for_scanner.scan(
118
+ /^[ \t]*domain:Domain-Name:[ \t]*((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+ma)[ \t]*$/i
119
+ ).flatten.map(&:downcase)
120
+ end
121
+
122
+ def domain_status_values
123
+ content_for_scanner.scan(/^[ \t]*Domain Status:[ \t]*(.+?)[ \t]*$/i).flatten
124
+ end
125
+
126
+ def absence_markers
127
+ content_for_scanner.scan(
128
+ /^[ \t]*(?:%error 230 No Objects Found|The queried object does not exist: No Object Found)[ \t]*$/i
129
+ )
130
+ end
131
+
132
+ def domain_name_field?
133
+ content_for_scanner.match?(/^[ \t]*(?:Domain Name:|domain:Domain-Name:)/i)
134
+ end
135
+
136
+ def record_details_present?
137
+ content_for_scanner.match?(
138
+ /^[ \t]*(?:Domain Status|Domain ID|Registrar|Creation Date|Updated Date|Registry Expiry Date|Name Server|domain:Class-Name|domain:ID|domain:Auth-Area):/i
139
+ )
140
+ end
141
+
62
142
  end
63
143
 
64
144
  end
@@ -20,6 +20,7 @@ module Whois
20
20
  DISCLAIMER_MATCHES = [
21
21
  "TERMS OF USE:", # global
22
22
  "Terminos de Uso:", # whois.nic.hn
23
+ "Queda absolutamente prohibido su uso", # current whois.nic.hn footer
23
24
  "The data in the WHOIS database of Meridian", # whois.meridiantld.net
24
25
  "This information is provided", # whois.gg
25
26
  ]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whois-parser-next
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simone Carletti