{"record":{"id":"5b3174b6e12823bd","repo":"iOfficeAI/OfficeCLI","slug":"remote-what-at-url-exceeds-max-1024-1024","errorCode":null,"errorMessage":"Remote {what} at {url} exceeds {max / (1024 * 1024)} MB limit.","messagePattern":"Remote (.+?) at (.+?) exceeds (.+?) MB limit\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/SsrfGuard.cs","lineNumber":98,"sourceCode":"    /// <summary>\n    /// Copy <paramref name=\"src\"/> into memory, refusing once <paramref name=\"max\"/>\n    /// bytes have been read. Use after <see cref=\"CreateGuardedHandler\"/> so a\n    /// chunked / Content-Length-lying response cannot exhaust memory. Callers\n    /// should still pre-check <c>response.Content.Headers.ContentLength</c> to\n    /// fail fast when the server is honest about an oversized body.\n    /// </summary>\n    /// <param name=\"what\">Noun used in the refusal message, e.g. \"image\" or \"file\".</param>\n    public static byte[] ReadBounded(Stream src, long max, string url, string what = \"file\")\n    {\n        using var ms = new MemoryStream();\n        var buf = new byte[81920];\n        long total = 0;\n        int n;\n        while ((n = src.Read(buf, 0, buf.Length)) > 0)\n        {\n            total += n;\n            if (total > max)\n                throw new ArgumentException(\n                    $\"Remote {what} at {url} exceeds {max / (1024 * 1024)} MB limit.\");\n            ms.Write(buf, 0, n);\n        }\n        return ms.ToArray();\n    }\n\n    /// <summary>\n    /// True only for globally-routable addresses. Blocks loopback, private\n    /// (RFC1918), link-local (incl. 169.254.0.0/16 cloud-metadata), unique-local\n    /// IPv6 (fc00::/7), multicast and unspecified — the SSRF target ranges.\n    /// </summary>\n    public static bool IsPublicAddress(IPAddress address)\n    {\n        var addr = address.IsIPv4MappedToIPv6 ? address.MapToIPv4() : address;\n\n        if (IPAddress.IsLoopback(addr)) return false;\n        if (addr.Equals(IPAddress.Any) || addr.Equals(IPAddress.IPv6Any)) return false;\n","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/SsrfGuard.cs#L80-L116","documentation":"Thrown by SsrfGuard.ReadBounded once the remote body has streamed more than the byte cap (default SsrfGuard.MaxRemoteBytes = 100 MB). It bounds memory on a hostile or accidentally-huge response because the default HttpClient buffering ceiling (~2 GB) is far above any legitimate asset. Callers are expected to also pre-check Content-Length to fail fast on honest servers.","triggerScenarios":"An image=/data=/media=/model3d= URL whose response body exceeds the cap; a streaming/chunked response with no Content-Length that keeps sending; pointing at a large downloadable file by mistake.","commonSituations":"Linking to a full-resolution raw asset; a CDN returning a larger variant than expected; an unbounded server stream.","solutions":["Point at a smaller, appropriately-sized asset (resize images, trim data).","Pre-check response.Content.Headers.ContentLength against your limit and reject before streaming.","Host the file locally instead of fetching a huge remote blob."],"exampleFix":"// before (client side, before ReadBounded)\nvar resp = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);\nvar len = resp.Content.Headers.ContentLength ?? -1;\nif (len is 0 or -1 || len > SsrfGuard.MaxRemoteBytes) return; // fail fast on honest servers\nusing var s = await resp.Content.ReadAsStreamAsync();\nvar bytes = SsrfGuard.ReadBounded(s, SsrfGuard.MaxRemoteBytes, url);","handlingStrategy":"validation","validationCode":"// Pre-check Content-Length when the server is honest about size\nlong len = resp.Content.Headers.ContentLength ?? -1;\nif (len > SsrfGuard.MaxRemoteBytes)\n    throw new InvalidOperationException(\"remote asset too large before download\");","typeGuard":null,"tryCatchPattern":"try { bytes = SsrfGuard.ReadBounded(stream, max, url, \"image\"); }\ncatch (System.ArgumentException ex) when (ex.Message.Contains(\"MB limit\"))\n{ /* asset exceeded the size cap; use a smaller source */ }","preventionTips":["Always request ResponseHeadersRead and check Content-Length first.","Cap and downscale images at the source/CDN.","Reject chunked streams of unknown length for untrusted URLs."],"tags":["network","resource-limits","http","fetch","memory"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}