joliegg 0.1.0 → 0.2.0

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: 4f27a41cfa159b96583ae4631a134e57c2e87ae617c148747e68019e7cd2af91
4
- data.tar.gz: 5c1094ecae586a0ed3705d041737435a5a51d5a31361e50454d4d455e4c815a4
3
+ metadata.gz: 9191af5aa744f9251907a02c5951ab251e88e1398ba40f4f78103a38b94e7cf8
4
+ data.tar.gz: 226262ee89d48d83d33401b7328bb1160b8053d8380e07eb4d92604afdb9a0b6
5
5
  SHA512:
6
- metadata.gz: f266a1b303854cf4178440d6004a397d44d4b7b3ced2faccea9bdd718f57f4e2519d5fab919b23a155b8bb7f7f632fb794fc52bb9f3198758f9f62e011f16a70
7
- data.tar.gz: 46101a1d04b5fc80feb86c89172be49f15606b6f4245874ae0e903217a296c428ddc252ad2fd0a6c91ca766d49e3e5c3b3dc036fea9c5976ba837c2f7734ff6a
6
+ metadata.gz: 7a481cda89dd46a9294df6165357656feac6f502610fecb3dfbc37d7c795dae59b8768cc99bb3a1cad75df7ceca6d0b6229c7181488203499fda7f11a816eeb9
7
+ data.tar.gz: 2a137407c21298fb394312b8cb916f16cc6dfff2c907bdb6fdb9465d3ab40091a9f14e00a170c55dbbb0a3d9571fc3616af9739297016eefefca6e2553428fbd
@@ -43,37 +43,48 @@ module Jolie
43
43
  find_in(shorteners, input)
44
44
  end
45
45
 
46
- # Reduces a URL, bare domain, or email address to a bare lowercase host:
46
+ # Reduces a URL, bare domain, or email address to its primary host:
47
47
  # "User@Sub.Example.COM" -> "sub.example.com"
48
48
  # "HTTPS://example.com:8080/a?b" -> "example.com"
49
49
  # "a/b@mailinator.com" -> "mailinator.com"
50
50
  # "example.com." -> "example.com"
51
- # Returns "" when nothing host-like can be extracted. IP literals pass
52
- # through unchanged (the lists hold domains, so they simply never match).
53
- #
54
- # A schemeless input containing @ is treated as an email address FIRST:
55
- # the local part may legally contain / ? # (RFC 5322 atext), so applying
56
- # URL delimiters to it would sever the domain ("a/b@mailinator.com" would
57
- # otherwise read as host "a"). The host is everything after the LAST @.
58
- #
59
- # With a scheme:// present it's a URL: the authority is isolated BEFORE
60
- # any @ handling, so an @ in the path/query/fragment
61
- # ("/redirect?to=user@mailinator.com") is never mistaken for userinfo,
62
- # and only the LAST @ within the authority is the userinfo delimiter (per
63
- # RFC 3986), so "example.com@evil.example" still resolves to
64
- # "evil.example".
51
+ # Returns "" when nothing host-like can be extracted. This is the FIRST
52
+ # candidate host; matching uses candidate_hosts, which can hold both
53
+ # readings of an ambiguous input.
65
54
  def extract_host(input)
66
- candidate = input.to_s.strip.downcase
67
-
68
- if !candidate.match?(%r{\A[a-z][a-z0-9+.-]*://}) && candidate.include?("@")
69
- return candidate.split("@").last.to_s.sub(/:\d*\z/, "").sub(/\.\z/, "")
70
- end
55
+ candidate_hosts(input).first.to_s
56
+ end
71
57
 
72
- candidate = candidate.sub(%r{\A[a-z][a-z0-9+.-]*://}, "") # scheme
73
- candidate = candidate.split(%r{[/\\?#]}).first.to_s # authority: cut path/query/fragment
74
- candidate = candidate.split("@").last.to_s # URL userinfo
75
- candidate = candidate.sub(/:\d*\z/, "") # port
76
- candidate.sub(/\.\z/, "") # trailing root dot
58
+ # Every host an input may resolve to, deduplicated, most likely reading
59
+ # first. Whitespace is stripped EVERYWHERE (not just the ends), matching
60
+ # the normalization URL validators and crawlers apply before resolving —
61
+ # "https://mai linator.com" resolves to mailinator.com for them, so it
62
+ # must match here too.
63
+ #
64
+ # - With a scheme:// the URL authority only: an @ later in the URL is
65
+ # path/query content, not userinfo, and only the LAST @ within the
66
+ # authority is the userinfo delimiter (RFC 3986), so
67
+ # "example.com@evil.example" resolves to "evil.example".
68
+ # - Schemeless with an @ is ambiguous: an email whose local part may
69
+ # legally contain / ? # (RFC 5322 atext — "a/b@mailinator.com"), or a
70
+ # bare URL carrying an @ ("mailinator.com/path@allowed.example", whose
71
+ # crawled host is mailinator.com). Both readings are returned so
72
+ # neither interpretation slips a listed host past the guard.
73
+ # - Otherwise the bare host itself.
74
+ def candidate_hosts(input)
75
+ candidate = input.to_s.downcase.gsub(/\s+/, "")
76
+ return [] if candidate.empty?
77
+
78
+ hosts =
79
+ if candidate.match?(%r{\A[a-z][a-z0-9+.-]*://})
80
+ [authority_host(candidate)]
81
+ elsif candidate.include?("@")
82
+ [candidate.split("@").last.to_s, authority_host(candidate)]
83
+ else
84
+ [authority_host(candidate)]
85
+ end
86
+
87
+ hosts.map { |host| normalize_host(host) }.reject(&:empty?).uniq
77
88
  end
78
89
 
79
90
  def blocklist
@@ -98,18 +109,40 @@ module Jolie
98
109
  @shorteners = nil
99
110
  end
100
111
 
112
+ # The candidate host that produced a blocklist match, or nil — e.g.
113
+ # "sub.mailinator.com" for a match on entry "mailinator.com".
114
+ def matched_host(input)
115
+ match_in(blocklist, input)&.last
116
+ end
117
+
101
118
  def find_in(set, input)
102
- host = extract_host(input)
103
- return nil if host.empty?
119
+ match_in(set, input)&.first
120
+ end
104
121
 
105
- parts = host.split(".")
106
- (parts.length - 1).downto(0) do |index|
107
- suffix = parts[index..].join(".")
108
- return suffix if set.include?(suffix)
122
+ def match_in(set, input)
123
+ candidate_hosts(input).each do |host|
124
+ parts = host.split(".")
125
+ (parts.length - 1).downto(0) do |index|
126
+ suffix = parts[index..].join(".")
127
+ return [suffix, host] if set.include?(suffix)
128
+ end
109
129
  end
110
130
  nil
111
131
  end
112
132
 
133
+ # The host out of a URL authority: strips any scheme, cuts path/query/
134
+ # fragment, and keeps only what follows the LAST @ (userinfo delimiter).
135
+ def authority_host(candidate)
136
+ candidate
137
+ .sub(%r{\A[a-z][a-z0-9+.-]*://}, "")
138
+ .split(%r{[/\\?#]}).first.to_s
139
+ .split("@").last.to_s
140
+ end
141
+
142
+ def normalize_host(host)
143
+ host.sub(/:\d*\z/, "").sub(/\.\z/, "")
144
+ end
145
+
113
146
  def load_set(path)
114
147
  Set.new(JSON.parse(File.read(path)))
115
148
  end
data/lib/jolie/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jolie
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: joliegg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Diana Islas Ocampo