{"record":{"id":"efbdea58611a7e50","repo":"apache/seatunnel","slug":"path-traversal-attempt-blocked-requested-re","errorCode":null,"errorMessage":"Path traversal attempt blocked - Requested: {}, Resolved: {}, LogDir: {}","messagePattern":"Path traversal attempt blocked - Requested: (.+?), Resolved: (.+?), LogDir: (.+?)","errorType":"console","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/servlet/LogBaseServlet.java","lineNumber":64,"sourceCode":"     * @param resp response used to return status and log content\n     * @param logPath configured log directory\n     * @param logName requested log file name from the request URI\n     */\n    protected void prepareLogResponse(HttpServletResponse resp, String logPath, String logName) {\n        if (StringUtils.isBlank(logPath)) {\n            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            log.warn(\n                    \"Log file path is empty, no log file path configured in the current configuration file\");\n            return;\n        }\n        String logFilePath = new File(logPath, logName).getPath();\n        try {\n            String canonicalLogDir = new File(logPath).getCanonicalPath();\n            String canonicalFilePath = new File(logFilePath).getCanonicalPath();\n            if (!canonicalFilePath.startsWith(canonicalLogDir + File.separator)\n                    && !canonicalFilePath.equals(canonicalLogDir)) {\n                resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n                log.warn(\n                        \"Path traversal attempt blocked - Requested: {}, Resolved: {}, LogDir: {}\",\n                        logName,\n                        canonicalFilePath,\n                        canonicalLogDir);\n                return;\n            }\n            String logContent = FileUtils.readFileToStr(new File(canonicalFilePath).toPath());\n            write(resp, logContent);\n        } catch (IOException e) {\n            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            log.warn(\"Failed to resolve log file path: {}, error: {}\", logFilePath, e.getMessage());\n        } catch (SeaTunnelRuntimeException e) {\n            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);\n            log.warn(String.format(\"Log file content is empty, get log path : %s\", logFilePath));\n        }\n    }\n}\n","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/servlet/LogBaseServlet.java#L46-L82","documentation":"This warning is logged by LogBaseServlet when a REST request for a node log file resolves (after canonicalization) to a path outside the configured log directory. It is a security guard against path traversal attacks like `?logName=../../../etc/passwd`. The servlet returns HTTP 400 and refuses to read the file.","triggerScenarios":"Calling the REST log endpoint (e.g. GET /log/<nodeName>) with a logName containing '..' segments, absolute paths, or symlinks that resolve outside the SeaTunnel log directory (log.path config).","commonSituations":"Automated scripts passing raw user input as logName; misconfigured reverse proxies appending path segments; symlinked log files pointing outside the log dir; probing by security scanners.","solutions":["Remove path traversal characters ('..', leading '/', URL-encoded variants) from the logName parameter before calling the endpoint","URL-encode the logName properly and pass only the plain file name of a log that exists inside the log directory","If logs legitimately live outside log.path, move/symlink them into the configured log directory or adjust the log.path configuration","Check audit logs — if unintended, this may indicate a security scan or attack and the request should be blocked"],"exampleFix":"// before\nGET /log/../../etc/passwd\n// after\nGET /log/seatunnel-worker-1.log","handlingStrategy":"validation","validationCode":"function isSafeLogName(name) {\n  if (!name || name.includes('..') || name.startsWith('/')) {\n    throw new Error('Unsafe log name: ' + name);\n  }\n  return true;\n}\nisSafeLogName(logName); // call before GET /log/<name>","typeGuard":"const isPlainFileName = (s) => typeof s === 'string' && /^[\\w.\\-]+$/.test(s);","tryCatchPattern":null,"preventionTips":["Never pass user-supplied path input directly as logName","Allow only plain file names matching a whitelist regex like [\\w.-]+","URL-encode query parameters correctly","Treat 400 responses on log endpoints as possible traversal attempts and alert"],"tags":["security","rest-api","path-traversal"],"backgroundTag":"path-traversal-blocked","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}