{"record":{"id":"c314b9b7aa1b6aa7","repo":"Kareadita/Kavita","slug":"invalid-filename","errorCode":"invalid-filename","errorMessage":"invalid-filename","messagePattern":"invalid-filename","errorType":"exception","errorClass":"KavitaException","httpStatus":400,"severity":"warning","filePath":"Kavita.Server/Controllers/UploadController.cs","lineNumber":365,"sourceCode":"    {\n        if (!IsPathWithinDirectory(_directoryService.TempDirectory, fileName)) return null;\n\n        var path = _directoryService.FileSystem.Path.Join(_directoryService.TempDirectory, fileName);\n        return _directoryService.FileSystem.File.Exists(path) ? path : null;\n    }\n\n    private async Task<string> CreateThumbnail(UploadCoverFileDto uploadCoverFileDto, string filename)\n    {\n        var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();\n        var encodeFormat = settings.EncodeMediaAs;\n        var (width, height) = settings.CoverImageSize.GetDimensions();\n\n        // Preferred path: the image was already streamed into temp (upload-by-url / upload-by-file) and we only\n        // received its filename. This avoids posting a large base64 payload back through the request body.\n        if (!string.IsNullOrEmpty(uploadCoverFileDto.FileName))\n        {\n            var tempPath = ResolveTempCoverPath(uploadCoverFileDto.FileName)\n                           ?? throw new KavitaException(await _localizationService.TranslateAsync(UserId, \"invalid-filename\"));\n\n            return _imageService.CreateThumbnailFromFile(tempPath, filename, encodeFormat, width, height);\n        }\n\n        // Legacy fallback: base64 payload\n        return _imageService.CreateThumbnailFromBase64(uploadCoverFileDto.Url, filename, encodeFormat, width, height);\n    }\n\n    /// <summary>\n    /// Replaces chapter cover image and locks it with a base64 encoded image. This will update the parent volume's cover image.\n    /// </summary>\n    /// <param name=\"uploadCoverFileDto\"></param>\n    /// <returns></returns>\n    [Authorize(Policy = PolicyGroups.AdminPolicy)]\n    [RequestSizeLimit(ControllerConstants.MaxUploadSizeBytes)]\n    [HttpPost(\"chapter\")]\n    public async Task<ActionResult> UploadChapterCoverImageFromUrl(UploadCoverFileDto uploadCoverFileDto)\n    {","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Server/Controllers/UploadController.cs#L347-L383","documentation":"Thrown by UploadController.CreateThumbnail (localized key 'invalid-filename') when ResolveTempCoverPath returns null — i.e. the FileName either escapes the temp directory (IsPathWithinDirectory fails, blocking path traversal) or does not point to an existing staged file. A KavitaException surfacing as HTTP 500 unless the endpoint catches it.","triggerScenarios":"Upload-by-filename cover replace where FileName is missing from temp, contains traversal (../), points outside TempDirectory, or the staged temp upload expired/was cleared before the thumbnail step.","commonSituations":"Client sends a filename without first streaming the file to temp; path traversal attempt; temp dir cleared between upload and thumbnail creation; wrong filename casing/path from the client.","solutions":["Always stage the file into Kavita's temp directory first (upload-by-file/upload-by-url), then send only its base filename.","Send the bare filename, not an absolute or relative path.","Ensure the temp upload completes and persists until the thumbnail request runs."],"exampleFix":"// before\n{ \"fileName\": \"../../../etc/passwd\" }\n\n// after\n{ \"fileName\": \"a1b2c3-cover.jpg\" }  // previously staged into temp","handlingStrategy":"validation","validationCode":"var path = ResolveTempCoverPath(dto.FileName);\nif (path is null) return BadRequest(\"invalid-filename\");","typeGuard":"static bool IsSafeTempFileName(string? name)\n    => !string.IsNullOrWhiteSpace(name)\n       && Path.GetFileName(name) == name\n       && !name.Contains(\"..\");","tryCatchPattern":"try { var p = await CreateThumbnail(dto, file); }\ncatch (KavitaException ex) { return BadRequest(ex.Message); }","preventionTips":["Stage the file to temp first, then send only the base filename.","Never send absolute or relative paths as FileName.","Confirm the temp upload persists until thumbnail creation."],"tags":["upload","validation","security","path-traversal","cover-image"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}