fix(app): harden account and workflow state
This commit is contained in:
@@ -72,7 +72,7 @@ public sealed class FollowUpReminderHostedService(
|
||||
if (!decision.NeedsFollowUp && !upcoming) continue;
|
||||
|
||||
var owner = await users.FindByIdAsync(job.OwnerUserId);
|
||||
if (owner is null || !owner.EmailConfirmed || string.IsNullOrWhiteSpace(owner.Email)) continue;
|
||||
if (owner is null || !owner.EmailFollowUpRemindersEnabled || !owner.EmailConfirmed || string.IsNullOrWhiteSpace(owner.Email)) continue;
|
||||
|
||||
var followMode = SuggestFollowUpMode(job.Status);
|
||||
var detailsUrl = externalOrigin.BuildPath($"/jobs?open={job.Id}&tab=4&followMode={Uri.EscapeDataString(followMode)}");
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace JobTrackerApi.Services.JobImport;
|
||||
|
||||
public sealed class JobImportService
|
||||
{
|
||||
private const int MaxDownloadBytes = 4_000_000;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly UniversalJobParser _universal;
|
||||
private readonly IEnumerable<IJobSitePlugin> _plugins;
|
||||
@@ -124,10 +125,25 @@ public sealed class JobImportService
|
||||
// Still read: many sites omit content-type. Best-effort.
|
||||
}
|
||||
|
||||
// Cap to avoid huge downloads.
|
||||
var bytes = await res.Content.ReadAsByteArrayAsync(cancellationToken);
|
||||
if (bytes.Length > 4_000_000) return null;
|
||||
return System.Text.Encoding.UTF8.GetString(bytes);
|
||||
// Enforce the cap while streaming. Checking after ReadAsByteArrayAsync allowed an
|
||||
// arbitrarily large (and automatically decompressed) response to consume memory first.
|
||||
if (res.Content.Headers.ContentLength is > MaxDownloadBytes) return null;
|
||||
await using var body = await res.Content.ReadAsStreamAsync(cancellationToken);
|
||||
using var bounded = new MemoryStream(capacity: res.Content.Headers.ContentLength is > 0
|
||||
? (int)Math.Min(res.Content.Headers.ContentLength.Value, MaxDownloadBytes)
|
||||
: 0);
|
||||
var buffer = new byte[64 * 1024];
|
||||
var total = 0;
|
||||
while (true)
|
||||
{
|
||||
var remainingWithSentinel = MaxDownloadBytes - total + 1;
|
||||
var read = await body.ReadAsync(buffer.AsMemory(0, Math.Min(buffer.Length, remainingWithSentinel)), cancellationToken);
|
||||
if (read == 0) break;
|
||||
total += read;
|
||||
if (total > MaxDownloadBytes) return null;
|
||||
await bounded.WriteAsync(buffer.AsMemory(0, read), cancellationToken);
|
||||
}
|
||||
return System.Text.Encoding.UTF8.GetString(bounded.GetBuffer(), 0, total);
|
||||
}
|
||||
|
||||
private async Task<UrlValidationResult> ValidateUrlAsync(string? url, CancellationToken cancellationToken)
|
||||
|
||||
Reference in New Issue
Block a user