{"record":{"id":"4c13d4a57e97e3ae","repo":"SillyTavern/SillyTavern","slug":"bad-request-4c13d4","errorCode":null,"errorMessage":"Bad Request","messagePattern":"Bad Request","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"src/middleware/validateFileName.js","lineNumber":35,"sourceCode":" * @returns {import('express').RequestHandler} Middleware function\n */\nexport function getFileNameValidationFunction(fieldName) {\n    /**\n    * Validates the field in the request body.\n    * @param {import('express').Request} req Request object\n    * @param {import('express').Response} res Response object\n    * @param {import('express').NextFunction} next Next middleware\n    */\n    return function validateAvatarUrlMiddleware(req, res, next) {\n        if (req.body && fieldName in req.body && (typeof req.body[fieldName] === 'string' || hasToString(req.body[fieldName]))) {\n            if (forbiddenRegExp.test(req.body[fieldName])) {\n                console.error('An error occurred while validating the request body', {\n                    handle: req.user.profile.handle,\n                    path: req.originalUrl,\n                    field: fieldName,\n                    value: req.body[fieldName],\n                });\n                return res.sendStatus(400);\n            }\n        }\n\n        next();\n    };\n}\n\nconst avatarUrlValidationFunction = getFileNameValidationFunction('avatar_url');\nexport default avatarUrlValidationFunction;\n","sourceCodeStart":17,"sourceCodeEnd":45,"githubUrl":"https://github.com/SillyTavern/SillyTavern/blob/8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8/src/middleware/validateFileName.js#L17-L45","documentation":"getFileNameValidationFunction returns a middleware that inspects a named body field (default 'avatar_url') and rejects requests where the value contains path separators or null bytes, as defined by forbiddenRegExp (slash, backslash on Windows, and \\x00). On a match it logs the offending handle/path/field/value and returns HTTP 400. This is a path-traversal / null-byte-injection prevention guard applied before the value is used as a filename.","triggerScenarios":"A POST/PUT request whose JSON body contains { \"avatar_url\": \"../secret\" } or { \"avatar_url\": \"foo/bar.png\" } or a value with an embedded NUL byte, hitting a route guarded by avatarUrlValidationFunction (or any middleware produced by getFileNameValidationFunction with a different field name).","commonSituations":"A client sends a relative or absolute path in avatar_url instead of a bare filename; a malicious probe attempting directory traversal via the avatar field; a bug in a front-end uploader that includes the full selected file path in the request body.","solutions":["Send only a bare filename (no / or \\ or NUL) in the avatar_url field — strip path components client-side before submitting.","If the value is legitimately a URL, route it to a URL-handling endpoint, not a filename-validated one.","Inspect the server log line 'An error occurred while validating the request body' to see the exact rejected value and field."],"exampleFix":"// before — client sends a path\n{ \"avatar_url\": \"uploads/avatar.png\" }\n// after — send only the bare filename\n{ \"avatar_url\": \"avatar.png\" }","handlingStrategy":"validation","validationCode":"// Client side: strip path components before sending a filename field\nfunction toBareFilename(value) {\n  // keep only the last path segment and remove NUL bytes\n  return String(value).replace(/\\x00/g, '').split(/[\\\\/]/).pop();\n}\nbody.avatar_url = toBareFilename(body.avatar_url);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Send bare filenames only — never absolute or relative paths — in filename-validated fields.","Apply the same forbiddenRegExp (/[/\\x00]/ on Unix, /[/\\\\\\x00]/ on Windows) on the client to fail before the request.","Use dedicated URL fields for URLs and filename fields for filenames; do not mix them."],"tags":["validation","security","path-traversal","http","input-validation"],"backgroundTag":null,"analyzedSha":"8172dcd0ee672d3cd9a5e5f7af134f91a45cd2b8","analyzedAt":"2026-08-13T07:48:40.832Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}