atproto_auth 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/confidential_client/Gemfile.lock +1 -1
- data/lib/atproto_auth/http_client.rb +25 -10
- data/lib/atproto_auth/identity/resolver.rb +18 -11
- data/lib/atproto_auth/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50d239692b4a8497da937e550ef8a83e5e02a24c68e605d770ebaef774c585a7
|
4
|
+
data.tar.gz: 5904cc180dd65f4c50881164afb06b392082e87b6c1e5bd9609d7eabb0d6e057
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7605b04694e36a6384210cc2016247f37f4756076d92aaec75e7d6c3788564554a380afad6182aae721c4f175185bbce81eedd15c160102fc9ae5ee150881747
|
7
|
+
data.tar.gz: 8847fb8b7d3fc9476ab9fadd3870c20e9e06cdd309b8649f256c2dd54b991e495662f9331b8a2602a0abecda34fe77283de0a784c4cd42ff47799024c1077e4f
|
@@ -94,23 +94,38 @@ module AtprotoAuth
|
|
94
94
|
unless ALLOWED_SCHEMES.include?(uri.scheme)
|
95
95
|
raise SSRFError, "URL scheme must be one of: #{ALLOWED_SCHEMES.join(", ")}"
|
96
96
|
end
|
97
|
-
|
97
|
+
|
98
|
+
# Extract and validate host before any network activity
|
99
|
+
host = uri.host.to_s.strip
|
100
|
+
raise SSRFError, "URL must include host" if host.empty?
|
98
101
|
raise SSRFError, "URL must not include fragment" if uri.fragment
|
99
102
|
|
100
103
|
uri
|
101
104
|
end
|
102
105
|
|
103
106
|
def validate_ip!(uri)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
107
|
+
# Check if host is an IP address by trying to parse it
|
108
|
+
if uri.host =~ /^(\d{1,3}\.){3}\d{1,3}$/
|
109
|
+
begin
|
110
|
+
ip = IPAddr.new(uri.host)
|
111
|
+
raise SSRFError, "Request to forbidden IP address" if forbidden_ip?(ip)
|
112
|
+
rescue IPAddr::InvalidAddressError
|
113
|
+
# Not a valid IP, will be handled as hostname below
|
114
|
+
end
|
115
|
+
end
|
109
116
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
117
|
+
# Also check resolved IPs for hostnames
|
118
|
+
begin
|
119
|
+
ips = Resolv::DNS.new.getaddresses(uri.host)
|
120
|
+
ips.each do |x|
|
121
|
+
ip_addr = IPAddr.new(x.to_s)
|
122
|
+
raise SSRFError, "Request to forbidden IP address" if forbidden_ip?(ip_addr)
|
123
|
+
rescue IPAddr::InvalidAddressError
|
124
|
+
next
|
125
|
+
end
|
126
|
+
rescue Resolv::ResolvError
|
127
|
+
raise SSRFError, "Could not resolve hostname"
|
128
|
+
end
|
114
129
|
end
|
115
130
|
|
116
131
|
def forbidden_ip?(ip)
|
@@ -62,7 +62,11 @@ module AtprotoAuth
|
|
62
62
|
# @raise [ValidationError] if verification fails
|
63
63
|
def verify_pds_binding(did, pds_url)
|
64
64
|
info = get_did_info(did)
|
65
|
-
normalize_url(info[:pds])
|
65
|
+
if normalize_url(info[:pds]) != normalize_url(pds_url)
|
66
|
+
raise ValidationError, "PDS #{pds_url} is not authorized for DID #{did}"
|
67
|
+
end
|
68
|
+
|
69
|
+
true
|
66
70
|
rescue StandardError => e
|
67
71
|
raise ValidationError, "Failed to verify PDS binding: #{e.message}"
|
68
72
|
end
|
@@ -82,7 +86,11 @@ module AtprotoAuth
|
|
82
86
|
auth_server_url = resource_server.authorization_servers.first
|
83
87
|
|
84
88
|
# Compare normalized URLs
|
85
|
-
normalize_url(auth_server_url)
|
89
|
+
if normalize_url(auth_server_url) != normalize_url(issuer)
|
90
|
+
raise ValidationError, "Issuer #{issuer} is not authorized for DID #{did}"
|
91
|
+
end
|
92
|
+
|
93
|
+
true
|
86
94
|
rescue StandardError => e
|
87
95
|
raise ValidationError, "Failed to verify issuer binding: #{e.message}"
|
88
96
|
end
|
@@ -94,7 +102,13 @@ module AtprotoAuth
|
|
94
102
|
# @raise [ValidationError] if verification fails
|
95
103
|
def verify_handle_binding(handle, did)
|
96
104
|
info = get_did_info(did)
|
97
|
-
|
105
|
+
|
106
|
+
unless info[:document].has_handle?(handle)
|
107
|
+
raise ValidationError,
|
108
|
+
"Handle #{handle} does not belong to DID #{did}"
|
109
|
+
end
|
110
|
+
|
111
|
+
true
|
98
112
|
rescue StandardError => e
|
99
113
|
raise ValidationError, "Failed to verify handle binding: #{e.message}"
|
100
114
|
end
|
@@ -137,14 +151,7 @@ module AtprotoAuth
|
|
137
151
|
def extract_domain(handle)
|
138
152
|
# Remove @ prefix if present
|
139
153
|
handle = handle[1..] if handle.start_with?("@")
|
140
|
-
|
141
|
-
# Handle could be user.domain.com or domain.com format
|
142
|
-
# We just need the domain portion
|
143
|
-
if handle.count(".") == 1
|
144
|
-
handle
|
145
|
-
else
|
146
|
-
handle.split(".", 2)[1]
|
147
|
-
end
|
154
|
+
handle
|
148
155
|
end
|
149
156
|
|
150
157
|
def fetch_txt_records(domain)
|
data/lib/atproto_auth/version.rb
CHANGED