Module: DomainSanity

Defined in:
lib/domain_sanity.rb,
lib/domain_sanity/ip.rb,
lib/domain_sanity/idn.rb,
lib/domain_sanity/data.rb,
lib/domain_sanity/name.rb,
lib/domain_sanity/policy.rb,
lib/domain_sanity/reason.rb,
lib/domain_sanity/subject.rb,
lib/domain_sanity/version.rb

Overview

DomainSanity validates and inspects domain names the strict way a certificate authority must: RFC 1035 label rules, IDN/punycode handling, Public Suffix List and TLD checks, CA/Browser Forum aware wildcard rules, and IP / reserved-range / reverse-zone detection.

Scope: this is offline, structural validation only. It does NOT resolve DNS, check CAA records, verify that a name exists, or enforce certificate field lengths (e.g. CN <= 64). IDN conversion is IDNA2003-style punycode via SimpleIDN, not full UTS-46 (see DomainSanity::IDN). Those remain the caller's responsibility.

What "valid" means is governed by a Policy; pass policy: a preset symbol (:ca_baseline, :dns_zone, :lenient), a Hash of overrides, or a Policy. The module-level methods are the friendly front door. For several questions about one subject, build a typed Subject once with .analyze and reuse it.

Defined Under Namespace

Modules: Data, IDN, IP, Name, RegistrableTypeFacts Classes: Hostname, IPSubject, MalformedSubject, Policy, Reason, ReverseZone, Subject, Wildcard

Constant Summary collapse

VERSION =
"0.1.0"

Class Method Summary collapse

Class Method Details

.analyze(subject, policy: :ca_baseline) ⇒ Object

A typed, lazily-memoized Subject (Hostname / Wildcard / IPSubject / ReverseZone / MalformedSubject) exposing every fact about subject. Named .analyze (not .inspect) so it does not shadow Object#inspect.



103
104
105
# File 'lib/domain_sanity.rb', line 103

def analyze(subject, policy: :ca_baseline)
  Subject.for(subject, policy: policy)
end

.data_versionsObject

Provenance and staleness of the reference data (reserved-IP snapshot, PSL and IDNA gem versions).



109
110
111
# File 'lib/domain_sanity.rb', line 109

def data_versions
  Data.versions
end

.ip?(subject) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/domain_sanity.rb', line 67

def ip?(subject)
  IP.ip?(subject)
end

.mixed_script?(subject) ⇒ Boolean

True when any label mixes scripts in a homograph-suspicious way (Latin with Cyrillic, etc.). Opt in to enforcing this during validation with a policy of { require_single_script: true }.

Returns:

  • (Boolean)


96
97
98
# File 'lib/domain_sanity.rb', line 96

def mixed_script?(subject)
  IDN.mixed_script?(subject)
end

.public_ip?(subject) ⇒ Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/domain_sanity.rb', line 77

def public_ip?(subject)
  IP.public?(subject)
end

.public_suffix(subject, policy: :ca_baseline) ⇒ Object



63
64
65
# File 'lib/domain_sanity.rb', line 63

def public_suffix(subject, policy: :ca_baseline)
  Name.public_suffix(subject, policy: policy)
end

.reasons(subject, policy: :ca_baseline) ⇒ Object

Structured reasons subject is not a valid FQDN, as Reason objects (code, message, label). Empty array means valid.



38
39
40
# File 'lib/domain_sanity.rb', line 38

def reasons(subject, policy: :ca_baseline)
  Name.reasons(subject, policy: policy)
end

.registrable_domain(subject, policy: :ca_baseline) ⇒ Object



59
60
61
# File 'lib/domain_sanity.rb', line 59

def registrable_domain(subject, policy: :ca_baseline)
  Name.registrable_domain(subject, policy: policy)
end

.reserved_ip?(subject) ⇒ Boolean

Private or otherwise reserved (non-public) IP. The BRs forbid issuing for these.

Returns:

  • (Boolean)


73
74
75
# File 'lib/domain_sanity.rb', line 73

def reserved_ip?(subject)
  IP.reserved?(subject)
end

.reverse_zone?(subject) ⇒ Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/domain_sanity.rb', line 81

def reverse_zone?(subject)
  IP.reverse_zone?(subject)
end

.to_ascii(subject) ⇒ Object



85
86
87
# File 'lib/domain_sanity.rb', line 85

def to_ascii(subject)
  IDN.to_ascii(subject)
end

.to_unicode(subject) ⇒ Object



89
90
91
# File 'lib/domain_sanity.rb', line 89

def to_unicode(subject)
  IDN.to_unicode(subject)
end

.valid?(subject, policy: :ca_baseline) ⇒ Boolean

Structurally valid FQDN with a real public TLD under the given policy. Wildcards, IP literals, and reverse-zone names are excluded here.

Returns:

  • (Boolean)


32
33
34
# File 'lib/domain_sanity.rb', line 32

def valid?(subject, policy: :ca_baseline)
  Name.valid?(subject, policy: policy)
end

.valid_tld?(subject, policy: :ca_baseline) ⇒ Boolean

True when subject is itself a recognized public suffix (eTLD), e.g. "com" or "co.uk". A full name like "example.com" is not a suffix and returns false.

Returns:

  • (Boolean)


55
56
57
# File 'lib/domain_sanity.rb', line 55

def valid_tld?(subject, policy: :ca_baseline)
  Name.valid_tld?(subject, policy: policy)
end

.valid_wildcard?(subject, policy: :ca_baseline) ⇒ Boolean

Baseline-Requirements-valid wildcard: "*" is the whole leftmost label and the remainder is not a bare public suffix.

Returns:

  • (Boolean)


48
49
50
# File 'lib/domain_sanity.rb', line 48

def valid_wildcard?(subject, policy: :ca_baseline)
  Name.valid_wildcard?(subject, policy: policy)
end

.wildcard?(subject) ⇒ Boolean

Returns:

  • (Boolean)


42
43
44
# File 'lib/domain_sanity.rb', line 42

def wildcard?(subject)
  Name.wildcard?(subject)
end