[backend] Consistently enforce https URIs

This commit is contained in:
Laura Hausmann 2024-10-29 18:39:16 +01:00
parent b43e81b834
commit e391aebce5
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
3 changed files with 20 additions and 20 deletions

View file

@ -82,7 +82,7 @@ public class SearchController(
if (search.Resolve) if (search.Resolve)
{ {
if (search.Query!.StartsWith("https://") || search.Query.StartsWith("http://")) if (search.Query!.StartsWith("https://"))
{ {
if (pagination.Offset is not null and not 0) return []; if (pagination.Offset is not null and not 0) return [];
@ -146,7 +146,7 @@ public class SearchController(
{ {
var user = HttpContext.GetUserOrFail(); var user = HttpContext.GetUserOrFail();
if (search.Resolve && (search.Query!.StartsWith("https://") || search.Query.StartsWith("http://"))) if (search.Resolve && search.Query!.StartsWith("https://"))
{ {
if (pagination.Offset is not null and not 0) return []; if (pagination.Offset is not null and not 0) return [];

View file

@ -93,7 +93,7 @@ public class SearchController(
throw GracefulException.NotFound("No result found"); throw GracefulException.NotFound("No result found");
} }
if (target.StartsWith("http://") || target.StartsWith("https://")) if (target.StartsWith("https://"))
{ {
Note? noteHit = null; Note? noteHit = null;
User? userHit = null; User? userHit = null;

View file

@ -42,11 +42,11 @@ public class WebFingerService(
public async Task<WebFingerResponse?> ResolveAsync(string query) public async Task<WebFingerResponse?> ResolveAsync(string query)
{ {
(query, var proto, var domain) = ParseQuery(query); (query, var domain) = ParseQuery(query);
if (domain == config.Value.WebDomain || domain == config.Value.AccountDomain) if (domain == config.Value.WebDomain || domain == config.Value.AccountDomain)
throw new GracefulException(HttpStatusCode.BadRequest, "Can't run WebFinger for local user"); throw new GracefulException(HttpStatusCode.BadRequest, "Can't run WebFinger for local user");
var webFingerUrl = await GetWebFingerUrlAsync(query, proto, domain); var webFingerUrl = await GetWebFingerUrlAsync(query, domain);
using var cts = CancellationTokenSource.CreateLinkedTokenSource(appLifetime.ApplicationStopping); using var cts = CancellationTokenSource.CreateLinkedTokenSource(appLifetime.ApplicationStopping);
cts.CancelAfter(TimeSpan.FromSeconds(10)); cts.CancelAfter(TimeSpan.FromSeconds(10));
@ -73,20 +73,20 @@ public class WebFingerService(
throw new Exception("Failed to deserialize xml payload"); throw new Exception("Failed to deserialize xml payload");
} }
public static (string query, string proto, string domain) ParseQuery(string query) public static (string query, string domain) ParseQuery(string query)
{ {
string domain; string domain;
string proto;
query = query.StartsWith("acct:") ? $"@{query[5..]}" : query; query = query.StartsWith("acct:") ? $"@{query[5..]}" : query;
if (query.StartsWith("http://") || query.StartsWith("https://")) if (query.StartsWith("http://"))
throw GracefulException.BadRequest($"Invalid query scheme: {query}");
if (query.StartsWith("https://"))
{ {
var uri = new Uri(query); var uri = new Uri(query);
domain = uri.Host; domain = uri.Host;
proto = query.StartsWith("http://") ? "http" : "https";
} }
else if (query.StartsWith('@')) else if (query.StartsWith('@'))
{ {
proto = "https";
var split = query.Split('@'); var split = query.Split('@');
// @formatter:off // @formatter:off
@ -103,14 +103,14 @@ public class WebFingerService(
throw new GracefulException(HttpStatusCode.BadRequest, $"Invalid query: {query}"); throw new GracefulException(HttpStatusCode.BadRequest, $"Invalid query: {query}");
} }
return (query, proto, domain); return (query, domain);
} }
private async Task<string> GetWebFingerUrlAsync(string query, string proto, string domain) private async Task<string> GetWebFingerUrlAsync(string query, string domain)
{ {
var template = await GetWebFingerTemplateFromHostMetaXmlAsync(proto, domain) ?? var template = await GetWebFingerTemplateFromHostMetaXmlAsync(domain) ??
await GetWebFingerTemplateFromHostMetaJsonAsync(proto, domain) ?? await GetWebFingerTemplateFromHostMetaJsonAsync(domain) ??
$"{proto}://{domain}/.well-known/webfinger?resource={{uri}}"; $"https://{domain}/.well-known/webfinger?resource={{uri}}";
var finalQuery = query.StartsWith('@') ? $"acct:{query[1..]}" : query; var finalQuery = query.StartsWith('@') ? $"acct:{query[1..]}" : query;
var encoded = UrlEncoder.Default.Encode(finalQuery); var encoded = UrlEncoder.Default.Encode(finalQuery);
@ -118,11 +118,11 @@ public class WebFingerService(
} }
// Technically, we should be checking for rel=lrdd *and* type=application/jrd+json, but nearly all implementations break this, so we can't. // Technically, we should be checking for rel=lrdd *and* type=application/jrd+json, but nearly all implementations break this, so we can't.
private async Task<string?> GetWebFingerTemplateFromHostMetaXmlAsync(string proto, string domain) private async Task<string?> GetWebFingerTemplateFromHostMetaXmlAsync(string domain)
{ {
try try
{ {
var hostMetaUrl = $"{proto}://{domain}/.well-known/host-meta"; var hostMetaUrl = $"https://{domain}/.well-known/host-meta";
using var res = await client.SendAsync(httpRqSvc.Get(hostMetaUrl, ["application/xrd+xml"]), using var res = await client.SendAsync(httpRqSvc.Get(hostMetaUrl, ["application/xrd+xml"]),
HttpCompletionOption.ResponseHeadersRead); HttpCompletionOption.ResponseHeadersRead);
await using var stream = await res.Content.ReadAsStreamAsync(); await using var stream = await res.Content.ReadAsStreamAsync();
@ -145,11 +145,11 @@ public class WebFingerService(
} }
// See above comment as for why jrd+json is commented out. // See above comment as for why jrd+json is commented out.
private async Task<string?> GetWebFingerTemplateFromHostMetaJsonAsync(string proto, string domain) private async Task<string?> GetWebFingerTemplateFromHostMetaJsonAsync(string domain)
{ {
try try
{ {
var hostMetaUrl = $"{proto}://{domain}/.well-known/host-meta.json"; var hostMetaUrl = $"https://{domain}/.well-known/host-meta.json";
using var res = await client.SendAsync(httpRqSvc.Get(hostMetaUrl, ["application/jrd+json"]), using var res = await client.SendAsync(httpRqSvc.Get(hostMetaUrl, ["application/jrd+json"]),
HttpCompletionOption.ResponseHeadersRead); HttpCompletionOption.ResponseHeadersRead);
var deserialized = await res.Content.ReadFromJsonAsync<HostMetaResponse>(); var deserialized = await res.Content.ReadFromJsonAsync<HostMetaResponse>();
@ -171,7 +171,7 @@ public class WebFingerService(
try try
{ {
var hostMetaUrl = $"{proto}://{domain}/.well-known/host-meta"; var hostMetaUrl = $"https://{domain}/.well-known/host-meta";
using var res = await client.SendAsync(httpRqSvc.Get(hostMetaUrl, ["application/jrd+json"]), using var res = await client.SendAsync(httpRqSvc.Get(hostMetaUrl, ["application/jrd+json"]),
HttpCompletionOption.ResponseHeadersRead); HttpCompletionOption.ResponseHeadersRead);
var deserialized = await res.Content.ReadFromJsonAsync<HostMetaResponse>(); var deserialized = await res.Content.ReadFromJsonAsync<HostMetaResponse>();