whois-parser-next 0.4.6 → 0.4.7

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: 66e7767b738ae0cf7df9736dc9a6a7685f9ddf7d344506a3a4233accf905f0d2
4
- data.tar.gz: c15b010e832b1e52cebdb02fd473e8c75176a0f232c777ba385e39b17676b327
3
+ metadata.gz: bf39e6c3afbb1b6a0cb49c48cb3fb11cf6db7d0162aaba77c9027ca4d2fadc3f
4
+ data.tar.gz: 1134d08ab3b1bcef0551f8008c366234805614e86cbe02ffe90501f5203dae84
5
5
  SHA512:
6
- metadata.gz: 587e231a29331459c3d14a9cd655ef2fa0230b819cbbcde397be5465500fd4a8eef4b5742407f402ed203d7104a9dc235553795427ec1b2daa1d08a34a24e82e
7
- data.tar.gz: d4e01866484c46fcf88ef68889d677459ae34db5c0920dcabfc4bb2eaf390f16af8d5524436056a543154320742d51cc445dfb3553de4eb4386adc5c0ef511f1
6
+ metadata.gz: addd71819acd60b21142b60e419625ac8ebbed5b160b33e6cfe5562bcd064727e04b98c820f53bb98c1e1649158829b10b10d3c9ae7b900b47b25f57c1023043
7
+ data.tar.gz: 56a6dde832f4f0a9990e7753ea7f709a2bcba56d461d4eac0aa299bdd69b5896fd3b584647b6766267e621c4f6d52e42961a1fca1521c619b086f96f2dc80fcf
data/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  This project uses [Semantic Versioning 2.0.0](http://semver.org/).
4
4
 
5
+ #### Release 0.4.7 (2026-10-19)
6
+
7
+ - ADDED: Added strict parsing for the official `.gt` and `.com.gt` web WHOIS
8
+ response. Registration requires one result heading with a single valid domain
9
+ and `ACTIVO`; absence requires one `alert alert-danger` result with the same
10
+ domain and the registry's exact `no está registrado` marker. Duplicate,
11
+ contradictory, malformed, mismatched, incomplete, denied, throttled, or
12
+ challenge responses remain non-positive. The parser ignores scripts, styles,
13
+ comments, and unrelated page content; contacts and dates are not parsed.
14
+ - EVIDENCE: The official UVG registry lookup is
15
+ [`whois.php?dn=...`](https://www.gt/sitio/whois.php?dn={domain}). Bounded
16
+ 2026-10-19 observations returned registered results for `google.gt` and
17
+ `google.com.gt`, and exact absence results for generated eight-letter names
18
+ under both suffixes. The registry's
19
+ [policy](https://www.gt/sitio/registration_policy.php?lang=en) limits lookup
20
+ to individual names rather than lists of registered names. Regression
21
+ examples use only minimal synthetic HTML. This is a web WHOIS source; no
22
+ TCP/43 or RDAP service is claimed. See the
23
+ [dated evidence report](docs/audits/guatemala-web-whois-2026-10-19.md).
24
+
5
25
  #### Release 0.4.6 (2026-10-19)
6
26
 
7
27
  - ADDED: Added `.ga` parsing for ANINF's current French WHOIS response, requiring
@@ -1,6 +1,6 @@
1
1
  module Whois
2
2
  class Parser
3
3
  # The current library version.
4
- VERSION = "0.4.6".freeze
4
+ VERSION = "0.4.7".freeze
5
5
  end
6
6
  end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'strscan'
5
+ require_relative 'base'
6
+ require_relative 'registry_response_safety'
7
+
8
+ module Whois
9
+ class Parsers
10
+ # The registry's per-domain public web WHOIS, observed 2026-10-19.
11
+ # Inspect only its result heading/alert, never navigation or script text.
12
+ class WwwGt < Base
13
+ include RegistryResponseSafety
14
+
15
+ property_supported(:status) { web_result[:status] }
16
+ property_supported(:domain) { web_result[:domain] }
17
+ property_supported(:registered?) { status == :registered }
18
+ property_supported(:available?) { status == :available }
19
+
20
+ def response_unavailable?
21
+ super || content_for_scanner.match?(/<cap-widget\b|\bclass=["']g-recaptcha["']|cf-chl-/i)
22
+ end
23
+
24
+ private
25
+
26
+ def web_result
27
+ @web_result ||= classify_web_result
28
+ end
29
+
30
+ def classify_web_result
31
+ blocks = result_blocks
32
+ return { status: :unknown } unless blocks
33
+
34
+ candidates = blocks.filter_map do |kind, text|
35
+ text = CGI.unescapeHTML(text.gsub('&aacute;', 'á').gsub('&Aacute;', 'Á')).gsub(/[ \t\r\n]+/, ' ').strip
36
+ if kind == 'h3' && (match = /\A(\S+)\. ACTIVO\z/i.match(text))
37
+ [match[1].downcase, :registered]
38
+ elsif kind == 'div' && (match = /\A(\S+)\. no está registrado \.\z/i.match(text))
39
+ [match[1].downcase, :available]
40
+ end
41
+ end
42
+ return { status: :unknown } unless candidates.size == 1
43
+
44
+ name, state = candidates.first
45
+ return { status: :unknown } unless name.bytesize <= 253 &&
46
+ name.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+gt\z/)
47
+
48
+ { domain: name, status: state }
49
+ end
50
+
51
+ # One forward scan, including malformed tags. Limit each tag before
52
+ # inspecting attributes and skip scripts, styles and comments entirely.
53
+ def result_blocks
54
+ input = StringScanner.new(content_for_scanner)
55
+ blocks = []
56
+ active = nil
57
+ depth = 0
58
+ text = +''
59
+ until input.eos?
60
+ chunk = input.scan_until(/</)
61
+ unless chunk
62
+ text << input.rest if active
63
+ input.terminate
64
+ break
65
+ end
66
+ text << chunk[0...-1] if active
67
+ if input.peek(3) == '!--'
68
+ return nil unless input.scan_until(/-->/)
69
+ next
70
+ end
71
+ tag = input.scan_until(/>/)
72
+ return nil unless tag && tag.bytesize <= 16_384
73
+ match = /\A[ \t]*(\/?)[ \t]*([a-z0-9]+)/i.match(tag)
74
+ next unless match
75
+ closing = match[1] == '/'
76
+ name = match[2].downcase
77
+ if !closing && %w[script style].include?(name)
78
+ return nil if active
79
+ return nil unless input.scan_until(%r{</#{name}[ \t\r\n]*>}i)
80
+ next
81
+ end
82
+ if active
83
+ depth += 1 if !closing && name == active
84
+ depth -= 1 if closing && name == active
85
+ if depth.zero?
86
+ blocks << [active, text]
87
+ active = nil
88
+ text = +''
89
+ else
90
+ text << ' '
91
+ end
92
+ elsif !closing && (name == 'h3' || (name == 'div' && alert_classes?(tag)))
93
+ active = name
94
+ depth = 1
95
+ end
96
+ end
97
+ active ? nil : blocks
98
+ end
99
+
100
+ def alert_classes?(tag)
101
+ value = tag[/\bclass[ \t]*=[ \t]*["']([^"']*)["']/i, 1]
102
+ classes = value.to_s.split
103
+ classes.include?('alert') && classes.include?('alert-danger')
104
+ end
105
+ end
106
+ end
107
+ end
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.4.6
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simone Carletti
@@ -753,6 +753,7 @@ files:
753
753
  - lib/whois/parsers/whois.za.org.rb
754
754
  - lib/whois/parsers/whois.zicta.zm.rb
755
755
  - lib/whois/parsers/whois1.nic.bi.rb
756
+ - lib/whois/parsers/www.gt.rb
756
757
  - lib/whois/parsers/za_central_registry.rb
757
758
  - lib/whois/safe_record.rb
758
759
  - lib/whois/scanners/base.rb