[Fix][Zeta] Fix path traversal vulnerability in log file REST API endpoints#10628
[Fix][Zeta] Fix path traversal vulnerability in log file REST API endpoints#10628davidzollo wants to merge 4 commits intoapache:devfrom
Conversation
Issue 1: Missing test cases for path traversal attacksLocation: Related context:
Problem description: Potential risks:
Impact scope:
Severity: MAJOR Improvement suggestions: // Add test method in JobLogIT.java:
@Test
public void testPathTraversalAttackPrevention() throws IOException, InterruptedException {
// Test Unix path traversal
Container.ExecResult result = server.execInContainer("sh", "-c",
"curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/log/../../etc/passwd");
Assertions.assertEquals("400", result.getStdout().trim(),
"Path traversal attack should be blocked with HTTP 400");
// Test Windows style path traversal (if applicable)
result = server.execInContainer("sh", "-c",
"curl -s -o /dev/null -w '%{http_code}' 'http://localhost:8080/log/..\\..\\..\\..\\etc\\passwd'");
Assertions.assertEquals("400", result.getStdout().trim(),
"Windows-style path traversal should also be blocked");
// Test symlink attack (if operable in container)
// server.execInContainer("sh", "-c", "ln -s /etc/passwd /tmp/seatunnel/logs/evil.log");
// result = server.execInContainer("sh", "-c",
// "curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/log/evil.log");
// Assertions.assertEquals("400", result.getStdout().trim());
}Rationale: Security fixes must be verified through automated testing to prevent regression Issue 2: Incomplete JavaDoc commentsLocation:
Related context:
Problem description:
Potential risks:
Impact scope:
Severity: MINOR Improvement suggestions: /**
* Prepares log file response with path traversal protection.
*
* <p>This method validates that the requested log file is within the configured
* log directory boundary to prevent CWE-22 path traversal attacks.
*
* <p>The validation is performed by:
* <ol>
* <li>Resolving both log directory and requested file to canonical paths
* (which resolves all {@code ..} and symbolic links)</li>
* <li>Verifying the canonical file path starts with the canonical log directory
* followed by {@link File#separator}</li>
* </ol>
*
* <p>Requests attempting to access files outside the log directory are rejected
* with HTTP 400 and a warning is logged.
*
* @param httpGetCommand the HTTP GET command to send response
* @param logPath the configured log directory path (must not be null/empty)
* @param logName the requested log file name from URI parameter
* @throws IOException if canonical path resolution fails (results in HTTP 400)
*/
private void prepareLogResponse(HttpGetCommand httpGetCommand, String logPath, String logName) {
// ... implementation
}Rationale: Security-critical code must include detailed documentation to ensure long-term maintainability Issue 3: Security audit logs not detailed enoughLocation:
Related context:
Problem description:
Potential risks:
Impact scope:
Severity: MINOR Improvement suggestions: // RestHttpGetCommandProcessor.java
if (!canonicalFilePath.startsWith(canonicalLogDir + File.separator)
&& !canonicalFilePath.equals(canonicalLogDir)) {
// Enhanced audit logging
String clientAddress = httpGetCommand.getSocketAddress().toString();
logger.warning(String.format(
"Path traversal attempt blocked - Client: %s, Requested path: %s, Resolved path: %s, Log directory: %s",
clientAddress, logName, canonicalFilePath, canonicalLogDir));
httpGetCommand.send400();
return;
}
// LogBaseServlet.java
if (!canonicalFilePath.startsWith(canonicalLogDir + File.separator)
&& !canonicalFilePath.equals(canonicalLogDir)) {
String clientAddress = req.getRemoteAddr();
String requestUri = req.getRequestURI();
log.warn("Path traversal attempt blocked - Client: {}, URI: {}, Requested: {}, Resolved: {}, LogDir: {}",
clientAddress, requestUri, logName, canonicalFilePath, canonicalLogDir);
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}Rationale: Security incidents should record complete audit trail information Issue 4: Missing specific handling for
|
dybyte
left a comment
There was a problem hiding this comment.
Looks good overall. Could you please add some regression tests for path traversal cases (e.g., "../../etc/passwd")?
| server.execInContainer( | ||
| "sh", | ||
| "-c", | ||
| "curl -s -o /dev/null -w '%{http_code}' 'http://localhost:8080/log/../../etc/passwd'"); |
There was a problem hiding this comment.
Could we add --path-as-is here? curl may normalize /../ by default, so the traversal payload might not reach the server unchanged.
| server.execInContainer( | ||
| "sh", | ||
| "-c", | ||
| "curl -s -o /dev/null -w '%{http_code}' 'http://localhost:8080/log/../../../etc/shadow'"); |
| server.execInContainer( | ||
| "sh", | ||
| "-c", | ||
| "curl -s -o /dev/null -w '%{http_code}' 'http://localhost:8080/logs/../../etc/passwd'"); |
dybyte
left a comment
There was a problem hiding this comment.
CI didn’t pass. Let’s hold off on merging.
|
@davidzollo Please check the CI |
|
I pulled this PR locally and traced both log-file REST paths ( The canonical-path check now validates that the final target stays under the configured log directory before any file read, and the E2E coverage exercises both |
DanielLeens
left a comment
There was a problem hiding this comment.
I pulled the latest HEAD locally and checked both log-serving entry points together.
The canonical-path guard is now enforced in both RestHttpGetCommandProcessor.prepareLogResponse() and LogBaseServlet.prepareLogResponse(), so /log/... and /logs/... can no longer escape the configured log directory, and the new E2E covers the traversal cases. I do not see a blocking issue in the current revision.
|
@davidzollo Could you please sync this branch with the latest dev branch? It looks like the CI failed due to flaky tests. |
352579d to
182bfb0
Compare
…points Add canonical path validation to prepareLogResponse() in both RestHttpGetCommandProcessor (legacy Hazelcast path) and LogBaseServlet (Jetty servlet path) to prevent directory traversal attacks via the /hazelcast/rest/maps/log/<path> and /hazelcast/rest/maps/logs/<path> endpoints. The fix resolves the constructed file path to its canonical form and verifies it remains within the configured log directory boundary. Requests attempting to escape the log directory (e.g. ../../etc/passwd) are rejected with HTTP 400.
- Use File constructor for cross-platform path concatenation - Separate IOException and SeaTunnelRuntimeException catch blocks for better diagnostics - Enhance security audit logs with resolved path and log directory info - Add path traversal regression test covering /log and /logs endpoints Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b50d897 to
cb53259
Compare
Purpose
Fix a path traversal vulnerability (CWE-22) in the log file REST API endpoints that allows arbitrary file read outside the configured log directory.
Affected Endpoints
/hazelcast/rest/maps/log/<path>(current node log)/hazelcast/rest/maps/logs/<path>(all node log)Both the legacy Hazelcast REST handler and the Jetty servlet code paths are affected.
Root Cause
The
logNameparameter extracted from the request URI is concatenated directly into a file path without any sanitization. An attacker can use path traversal sequences (e.g.,../../etc/passwd) to read arbitrary files on the server.Fix
Add canonical path validation in
prepareLogResponse()for both code paths:RestHttpGetCommandProcessor(legacy Hazelcast handler)LogBaseServlet(Jetty servlet base class)The fix resolves the constructed file path to its canonical form via
File.getCanonicalPath()and verifies it remains within the configured log directory boundary. Requests attempting to escape the log directory are rejected with HTTP 400.Changed Files
seatunnel-engine/seatunnel-engine-server/.../rest/RestHttpGetCommandProcessor.javaseatunnel-engine/seatunnel-engine-server/.../rest/servlet/LogBaseServlet.javaCVE-2026-40119