whois-parser-next 0.3.0 → 0.3.2

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: 4dd7f05a922301b74d2dccf983d9712ac9611198244bb84bf7da9d3106eb6985
4
+ data.tar.gz: 474233b88fad6dad201ca87d0d5c91e5a15a8abab76904192710d644a74420db
5
5
  SHA512:
6
- metadata.gz: d9608362d4824d5c3a9044b3dd59e2c82935e915c88b938af75c9f8904852fab632b2e76132f25fd7a4faf7652ed63cdd54f86327c73e9efa1326df5d0225c14
7
- data.tar.gz: 8c93323b08ea3c950f809bd201026a4a2a5086f9d5fddfcb31dad8a2888a5857ad4810f3932b3995b1abf4adee44fa4bf6354f96191527b5799952f4508a40e2
6
+ metadata.gz: 0f041533d14ae1f57dfe6ea92e1967bb1ef5fdfb40379f4eea079c2bf37c58adadf53e8ed0685e0e55076ba9df44341b5a91f9115e59478a67f1c178d66fae76
7
+ data.tar.gz: 762d431b3ec1aa9b967a68b74f346ed41fbb37adbbb4f5304fb8e615dec298308f5f276f54a06e57da5b8a1bc217f6dd4b5f57c0df5e62c6142f1202d1069c41
data/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  This project uses [Semantic Versioning 2.0.0](http://semver.org/).
4
4
 
5
+ #### Release 0.3.2 (2026-09-25)
6
+
7
+ - ADDED: Added host-keyed parsers for the current IANA-listed WHOIS hosts for
8
+ `.mu` (`whois.tld.mu`), `.africa` (`whois.nic.africa`), and `.sr`
9
+ (`whois.sr`). These parsers recognize the documented current registered and
10
+ authoritative-absence response shapes, while empty, denied, throttled, and
11
+ ambiguous responses remain non-positive.
12
+ - LIMITATION: The pinned `whois` 6.0.3 client routes `.mu` and `.africa` to
13
+ different hosts and has no `.sr` mapping. The new parsers are selected only
14
+ for response parts keyed to their IANA-listed hosts; this release does not
15
+ add default-client routing or claim effective application coverage for
16
+ these TLDs.
17
+ - CORRECTION (2026-09-25): The corrected top-1,000 scorecard retracts the
18
+ previously published 175-pair total. Its bounded rowwise crosswalk
19
+ reproduces 130 directly traceable report-pair observations, with 31 package
20
+ rows remaining detail-insufficient. The later `.top`, `.uz`, and `.sa`
21
+ scorecard follow-ups, and the `.mu`, `.africa`, and `.sr` host-specific
22
+ reports in this release, remain outside that pinned baseline. These are
23
+ audit-evidence counts, not parser coverage or an effective-coverage rate.
24
+ - EVIDENCE: [`.mu` and `.africa` current-host report](docs/audits/top-201-250-current-host-followup-2026-09-25.md),
25
+ [`.sr` current-host report](docs/audits/topdomains-251-275-sr-followup-2026-09-25.md),
26
+ and [corrected scorecard](docs/audits/top-1000-effective-coverage-scorecard-2026-09-25.md).
27
+
28
+ #### Release 0.3.1 (2026-09-25)
29
+
30
+ - ADDED: Added paired registered and exact authoritative-absence parsing for
31
+ `.ma` and `.hn`. Empty and denied replies remain unavailable, rate limits
32
+ remain throttled, and conflicting or incomplete replies remain unknown.
33
+ - FIXED: Corrected `.pk` parsing for PKNIC's observed indented response
34
+ fields. Registration and availability still require explicit markers;
35
+ incomplete or conflicting replies remain unknown, and denied or throttled
36
+ replies remain non-positive.
37
+ - EVIDENCE: [Current `.ma` and `.hn` responses](docs/audits/top-126-150-tlds-2026-09-19.md)
38
+ and [production-shaped `.pk` responses](docs/audits/top1000-es-pk-shop-dev-app-2026-09-25.md).
39
+
5
40
 
6
41
  #### Release 0.3.0 (2026-09-25)
7
42
 
@@ -23,12 +58,17 @@ This project uses [Semantic Versioning 2.0.0](http://semver.org/).
23
58
  added unavailable-only handling for explicit unsupported responses from
24
59
  `.digital`, `.email`, `.live`, `.media`, `.network`, and `.services`.
25
60
  - DOCS: Added a scorecard for 1,000 observed TLD strings from the ICANN DNS
26
- Magnitude snapshot dated 2026-09-12. Its strict count of 175 paired rows is
27
- pinned to parser baseline `f2c822c` and excludes the Top 100, whose summary
28
- labels do not expose row-level evidence pairs. The scorecard does not claim
29
- 1,000 effective WHOIS integrations or a single effective-coverage rate.
30
- Later `.uz`/`.sa` evidence and `.pk`/`.ge` parsers are follow-ups, not
31
- additions to that pinned count.
61
+ Magnitude snapshot dated 2026-09-12. The original scorecard stated 175
62
+ paired rows and excluded the Top 100, whose summary labels do not expose
63
+ row-level evidence pairs. See the dated correction below; the scorecard
64
+ does not claim 1,000 effective WHOIS integrations or a single
65
+ effective-coverage rate.
66
+ - CORRECTION (2026-09-25): A rowwise crosswalk could not reproduce the
67
+ originally published 175-pair claim. The corrected scorecard and bounded
68
+ evidence ledger identify 130 directly traceable snapshot pair observations
69
+ and 31 package rows with insufficient row-level detail; later `.top`,
70
+ `.uz`, and `.sa` pairs are recorded separately. These are report evidence
71
+ counts, not effective parser coverage.
32
72
  - SOURCES: The audit notes link to the [ICANN DNS Magnitude
33
73
  snapshot](https://magnitude.research.icann.org/historic/20260912.full.html),
34
74
  [IANA `.top`](https://www.iana.org/domains/root/db/top.html),
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.2`. 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.2, 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.2", require: "whois-parser"
62
61
  ```
63
62
 
64
63
  ## Requirements
@@ -87,18 +86,23 @@ The [2026-09-25 scorecard](docs/audits/top-1000-effective-coverage-scorecard-202
87
86
  summarizes evidence for 1,000 observed TLD strings from the
88
87
  [ICANN DNS Magnitude snapshot dated 2026-09-12](https://magnitude.research.icann.org/historic/20260912.full.html).
89
88
  These strings are not 1,000 guaranteed working WHOIS registries or
90
- domain-availability results. Its strict paired count is pinned to baseline
91
- `f2c822c`, excludes the Top 100 whose summary labels do not expose row-level
92
- evidence pairs, and does not claim an effective-coverage percentage. The
93
- scorecard keeps verified parser evidence, explicit unavailable or unsupported
94
- responses, unknown or unresolved endpoints, and classification-only rows
95
- distinct. Later `.uz`/`.sa` paired evidence and `.pk`/`.ge` parsers are
96
- follow-ups, not changes to the pinned scorecard counts. The `.pk` parser does
97
- not make the default `whois` 6.0.3 Web adapter use port 43; `Whois::Client`
98
- host configuration does not override that adapter. The `.ge` parser targets
99
- IANA's `whois.nic.ge`, while the default `whois` route is
100
- `whois.registration.ge`. Both require explicit fixed-host routing before an
101
- application can count them as effective coverage.
89
+ domain-availability results. A dated correction retracts the previously
90
+ published 175 paired count: the hand-audited report crosswalk reproduces 130
91
+ direct row-pair observations (61 at ranks 101–300 and 69 at 301–1000), with 31
92
+ package rows remaining detail-insufficient. Three later pairs (`.top`, `.uz`,
93
+ `.sa`) are tracked separately from that pinned baseline. See the [corrected
94
+ scorecard](docs/audits/top-1000-effective-coverage-scorecard-2026-09-25.md),
95
+ [methodology](docs/audits/top1000-evidence-ledger-methodology-2026-09-25.md),
96
+ and [bounded machine-readable subset](docs/audits/data/whois-parser-top1000-evidence-ledger-subset-2026-09-25.csv).
97
+ No effective-coverage percentage is asserted. Summary-only, unsupported,
98
+ unknown, one-sided, and classification-only evidence is not promoted to a
99
+ pair. The `.pk` parser does not make the default `whois` 6.0.3 Web adapter use
100
+ port 43; `Whois::Client` host configuration does not override that adapter.
101
+ The `.ge` parser targets IANA's `whois.nic.ge`, while the default `whois` route
102
+ is `whois.registration.ge`. The `.mu` and `.africa` parsers also target IANA
103
+ hosts that differ from the default `whois` 6.0.3 mappings, and `.sr` has no
104
+ default mapping. These host-keyed parsers require separately verified routing
105
+ before an application can count them as effective coverage.
102
106
 
103
107
  Start with the [source verification report](docs/audits/top-1000-source-verification-2026-09-19.md),
104
108
  the [Top 20 review](docs/audits/top-20-tlds-2026-09-19.md), and the [Top 100
data/RELEASING.md CHANGED
@@ -21,8 +21,8 @@ RubyGems API key to the repository or its Actions secrets.
21
21
  Create and push the annotated release tag only after the checks above pass:
22
22
 
23
23
  ```shell
24
- git tag -a v0.3.0 -m "whois-parser-next 0.3.0"
25
- git push origin v0.3.0
24
+ git tag -a v0.3.2 -m "whois-parser-next 0.3.2"
25
+ git push origin v0.3.2
26
26
  ```
27
27
 
28
28
  The release workflow repeats the full suite and package smoke test before it
@@ -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.2".freeze
5
5
  end
6
6
  end
@@ -0,0 +1,23 @@
1
+ require_relative 'base_icann_compliant'
2
+
3
+ module Whois
4
+ class Parsers
5
+ # Parser for IANA's current .africa WHOIS endpoint, whois.nic.africa.
6
+ #
7
+ # The registry's exact `No information was found matching that query.`
8
+ # response is treated as absence. Other unrecognised responses remain
9
+ # unknown, and inherited response-safety checks preserve denials and
10
+ # throttling as errors.
11
+ class WhoisNicAfrica < BaseIcannCompliant
12
+ property_supported :available? do
13
+ content_for_scanner.match?(
14
+ /^[ \t]*No information was found matching that query\.[ \t]*$/i
15
+ )
16
+ end
17
+
18
+ property_supported :expires_on do
19
+ node("Registry Expiry Date") { |value| parse_time(value) }
20
+ end
21
+ end
22
+ end
23
+ 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
@@ -0,0 +1,44 @@
1
+ require_relative 'base_top1000_icann'
2
+
3
+ module Whois
4
+ class Parsers
5
+ # Parser for the IANA-listed WHOIS server for .SR.
6
+ #
7
+ # IANA's delegation record and current server responses were checked on
8
+ # 2026-09-25. The generated-name response uses an exact Message marker;
9
+ # other incomplete responses must remain unknown.
10
+ class WhoisSr < BaseTop1000Icann
11
+ property_supported :domain do
12
+ node("Domain", &:downcase)
13
+ end
14
+
15
+ property_supported :status do
16
+ if reserved?
17
+ :reserved
18
+ elsif available?
19
+ :available
20
+ elsif !node("Domain").to_s.strip.empty? &&
21
+ node("Status").to_s.casecmp("active").zero? &&
22
+ !node("Creation Date").to_s.strip.empty?
23
+ :registered
24
+ else
25
+ :unknown
26
+ end
27
+ end
28
+
29
+ property_supported :available? do
30
+ content_for_scanner.match?(/^Message: No Object Found\s*$/i)
31
+ end
32
+
33
+ property_supported :registered? do
34
+ status == :registered
35
+ end
36
+
37
+ property_supported :nameservers do
38
+ Array.wrap(node("Name server")).reject(&:empty?).map do |name|
39
+ Parser::Nameserver.new(name: name.downcase)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'base_icann_compliant'
2
+
3
+ module Whois
4
+ class Parsers
5
+ # Parser for the IANA-listed .mu WHOIS server, whois.tld.mu.
6
+ #
7
+ # Captures from 2026-09-25 expose ICANN-style records and the exact
8
+ # `Domain not found.` absence marker. This host is distinct from the
9
+ # legacy whois.nic.mu mapping in whois 6.0.3.
10
+ class WhoisTldMu < BaseIcannCompliant
11
+ self.scanner = Scanners::BaseIcannCompliant, {
12
+ pattern_available: /^[ \t]*Domain not found\.[ \t]*$/i,
13
+ }
14
+
15
+ property_supported :expires_on do
16
+ node("Registry Expiry Date") { |value| parse_time(value) }
17
+ end
18
+ end
19
+ end
20
+ 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.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simone Carletti
@@ -299,6 +299,7 @@ files:
299
299
  - lib/whois/parsers/whois.nic.accountant.rb
300
300
  - lib/whois/parsers/whois.nic.actor.rb
301
301
  - lib/whois/parsers/whois.nic.af.rb
302
+ - lib/whois/parsers/whois.nic.africa.rb
302
303
  - lib/whois/parsers/whois.nic.ag.rb
303
304
  - lib/whois/parsers/whois.nic.agency.rb
304
305
  - lib/whois/parsers/whois.nic.ai.rb
@@ -701,12 +702,14 @@ files:
701
702
  - lib/whois/parsers/whois.sgnic.sg.rb
702
703
  - lib/whois/parsers/whois.sk-nic.sk.rb
703
704
  - lib/whois/parsers/whois.smallregistry.net.rb
705
+ - lib/whois/parsers/whois.sr.rb
704
706
  - lib/whois/parsers/whois.srs.net.nz.rb
705
707
  - lib/whois/parsers/whois.sx.rb
706
708
  - lib/whois/parsers/whois.tcinet.ru.rb
707
709
  - lib/whois/parsers/whois.teleinfo.cn.rb
708
710
  - lib/whois/parsers/whois.thnic.co.th.rb
709
711
  - lib/whois/parsers/whois.tld.ee.rb
712
+ - lib/whois/parsers/whois.tld.mu.rb
710
713
  - lib/whois/parsers/whois.tld.sy.rb
711
714
  - lib/whois/parsers/whois.tonic.to.rb
712
715
  - lib/whois/parsers/whois.tonicregistry.to.rb