{"record":{"id":"ebcacf8a125d2c18","repo":"iOfficeAI/OfficeCLI","slug":"remote-file-exceeds-ssrfguard-maxremotebytes-1","errorCode":null,"errorMessage":"Remote file exceeds {SsrfGuard.MaxRemoteBytes / (1024 * 1024)} MB limit.","messagePattern":"Remote file exceeds (.+?) MB limit\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/FileSource.cs","lineNumber":109,"sourceCode":"    {\n        // SSRF guard: same connect-time public-IP enforcement as image fetch —\n        // refuse loopback / private / link-local / cloud-metadata targets. See\n        // SsrfGuard. Without this, a caller-supplied data=/model3d=/media= URL\n        // is an SSRF primitive when officecli runs on untrusted input.\n        var handler = SsrfGuard.CreateGuardedHandler(\"file\");\n\n        using var client = new HttpClient(handler, disposeHandler: true) { Timeout = TimeSpan.FromSeconds(30) };\n        client.DefaultRequestHeaders.Add(\"User-Agent\", \"OfficeCLI\");\n\n        var response = client.GetAsync(url).GetAwaiter().GetResult();\n        response.EnsureSuccessStatusCode();\n\n        // Bound memory use: fail fast on an honest oversized Content-Length, then\n        // read through the shared SsrfGuard.ReadBounded so a chunked / lying\n        // response can't exhaust memory either. Same cap as the image path.\n        var declared = response.Content.Headers.ContentLength;\n        if (declared is > SsrfGuard.MaxRemoteBytes)\n            throw new ArgumentException(\n                $\"Remote file exceeds {SsrfGuard.MaxRemoteBytes / (1024 * 1024)} MB limit.\");\n        var bytes = SsrfGuard.ReadBounded(\n            response.Content.ReadAsStream(), SsrfGuard.MaxRemoteBytes, url, \"file\");\n\n        // Try extension from URL path\n        var uri = new Uri(url);\n        var ext = Path.GetExtension(uri.AbsolutePath).ToLowerInvariant();\n\n        // Fallback: infer from content-type header\n        if (string.IsNullOrEmpty(ext))\n        {\n            var mime = response.Content.Headers.ContentType?.MediaType;\n            ext = MimeToExtension(mime);\n        }\n\n        return (new MemoryStream(bytes), ext);\n    }\n","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/FileSource.cs#L91-L127","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use a smaller asset, or compress/resize it below 100 MB before referencing it.","Host the large file locally and pass a filesystem path instead of a URL.","Confirm the URL resolves to the intended (small) file and isn't redirecting to a larger resource."],"exampleFix":"// before\nvar (stream, ext) = FileSource.Resolve(\"https://cdn.example.com/huge_video.mp4\");\n// after\nvar (stream, ext) = FileSource.Resolve(\"./local/small_clip.mp4\");","handlingStrategy":"validation","validationCode":"static async Task<long> HeadContentLengthAsync(string url)\n{\n    using var h = SsrfGuard.CreateGuardedHandler(\"file\");\n    using var c = new HttpClient(h) { Timeout = TimeSpan.FromSeconds(30) };\n    var resp = await c.SendAsync(new HttpRequestMessage(HttpMethod.Head, url));\n    return resp.Content.Headers.ContentLength ?? -1;\n}","typeGuard":null,"tryCatchPattern":"try { var r = FileSource.Resolve(url); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"MB limit\"))\n{ /* use a smaller/local asset */ }","preventionTips":["Keep remote assets under 100 MB.","HEAD the URL first to check Content-Length before fetching."],"tags":["file-source","network","size-limit","ssrf"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}