iOfficeAI/OfficeCLI · error · ArgumentException

Remote file exceeds {SsrfGuard.MaxRemoteBytes / (1024 * 1024

Error message

Remote file exceeds {SsrfGuard.MaxRemoteBytes / (1024 * 1024)} MB limit.

What it means

Thrown by FileSource.ResolveUrl when a remote file's declared Content-Length exceeds the 100 MB cap (SsrfGuard.MaxRemoteBytes). The fetch uses an SSRF-guarded handler and bounds memory by both the declared length and a bounded read of the actual stream, so an oversized or lying response is rejected.

Source

Thrown at src/officecli/Core/FileSource.cs:109

    {
        // SSRF guard: same connect-time public-IP enforcement as image fetch —
        // refuse loopback / private / link-local / cloud-metadata targets. See
        // SsrfGuard. Without this, a caller-supplied data=/model3d=/media= URL
        // is an SSRF primitive when officecli runs on untrusted input.
        var handler = SsrfGuard.CreateGuardedHandler("file");

        using var client = new HttpClient(handler, disposeHandler: true) { Timeout = TimeSpan.FromSeconds(30) };
        client.DefaultRequestHeaders.Add("User-Agent", "OfficeCLI");

        var response = client.GetAsync(url).GetAwaiter().GetResult();
        response.EnsureSuccessStatusCode();

        // Bound memory use: fail fast on an honest oversized Content-Length, then
        // read through the shared SsrfGuard.ReadBounded so a chunked / lying
        // response can't exhaust memory either. Same cap as the image path.
        var declared = response.Content.Headers.ContentLength;
        if (declared is > SsrfGuard.MaxRemoteBytes)
            throw new ArgumentException(
                $"Remote file exceeds {SsrfGuard.MaxRemoteBytes / (1024 * 1024)} MB limit.");
        var bytes = SsrfGuard.ReadBounded(
            response.Content.ReadAsStream(), SsrfGuard.MaxRemoteBytes, url, "file");

        // Try extension from URL path
        var uri = new Uri(url);
        var ext = Path.GetExtension(uri.AbsolutePath).ToLowerInvariant();

        // Fallback: infer from content-type header
        if (string.IsNullOrEmpty(ext))
        {
            var mime = response.Content.Headers.ContentType?.MediaType;
            ext = MimeToExtension(mime);
        }

        return (new MemoryStream(bytes), ext);
    }

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Use a smaller asset, or compress/resize it below 100 MB before referencing it.
  2. Host the large file locally and pass a filesystem path instead of a URL.
  3. Confirm the URL resolves to the intended (small) file and isn't redirecting to a larger resource.

Example fix

// before
var (stream, ext) = FileSource.Resolve("https://cdn.example.com/huge_video.mp4");
// after
var (stream, ext) = FileSource.Resolve("./local/small_clip.mp4");
Defensive patterns

Strategy: validation

Validate before calling

static async Task<long> HeadContentLengthAsync(string url)
{
    using var h = SsrfGuard.CreateGuardedHandler("file");
    using var c = new HttpClient(h) { Timeout = TimeSpan.FromSeconds(30) };
    var resp = await c.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));
    return resp.Content.Headers.ContentLength ?? -1;
}

Try / catch

try { var r = FileSource.Resolve(url); }
catch (ArgumentException ex) when (ex.Message.Contains("MB limit"))
{ /* use a smaller/local asset */ }

Prevention

When it happens

Trigger: Calling FileSource.Resolve (or a media/image/model3d fetch) with an http(s):// URL whose response Content-Length is greater than 100 MB (100 * 1024 * 1024 bytes). The check runs after a successful HTTP response but before reading the body.

Common situations: Pointing at a large asset (video, hi-res image, big model); a URL that returns a download/redirect to a much larger file; a misconfigured asset server.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/ebcacf8a125d2c18. Report an issue: GitHub.