apache/seatunnel · error · IllegalArgumentException

Resolved trace path escapes baseDir: ${traceDir}

Error message

Resolved trace path escapes baseDir: ${traceDir}

What it means

After resolving the trace directory as {baseDir}/traces/{jobId}/{date} and normalizing it, TraceFileWriter verifies the resulting path still starts with the normalized baseDir. If a crafted jobId or date (e.g. containing '..') resolves outside baseDir, this IllegalArgumentException is thrown — a path-traversal defense.

Source

Thrown at seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/event/TraceFileWriter.java:65

    private final BufferedWriter writer;
    private final AtomicLong eventCount;
    private final AtomicLong fileSize;
    private final AtomicBoolean closed = new AtomicBoolean(false);

    public TraceFileWriter(String baseDir, String jobId, String date) throws IOException {
        if (jobId == null || !JOB_ID_PATTERN.matcher(jobId).matches()) {
            throw new IllegalArgumentException("Invalid jobId for trace file path: " + jobId);
        }
        this.jobId = jobId;
        this.date = date;
        this.eventCount = new AtomicLong(0);
        this.fileSize = new AtomicLong(0);

        // Create directory: {baseDir}/traces/{jobId}/{date}/
        Path basePath = Paths.get(baseDir).toAbsolutePath().normalize();
        Path traceDir = basePath.resolve(Paths.get("traces", jobId, date)).normalize();
        if (!traceDir.startsWith(basePath)) {
            throw new IllegalArgumentException("Resolved trace path escapes baseDir: " + traceDir);
        }
        Files.createDirectories(traceDir);

        // Generate file name: traces-{HH-mm-ss}-{uuid}.jsonl
        String timestamp = LocalDateTime.now().format(TIME_FORMATTER);
        String shortUuid = UUID.randomUUID().toString().substring(0, 8);
        String fileName = String.format("traces-%s-%s.jsonl", timestamp, shortUuid);

        this.filePath = traceDir.resolve(fileName);

        // Create file with BufferedWriter
        this.writer =
                Files.newBufferedWriter(
                        filePath,
                        StandardCharsets.UTF_8,
                        StandardOpenOption.CREATE_NEW,
                        StandardOpenOption.WRITE);

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Validate jobId and date against strict patterns before constructing the writer (jobId must pass JOB_ID_PATTERN; date must be a plain date string)
  2. Use canonical, engine-generated identifiers only
  3. Check baseDir configuration points to an intended, existing directory
  4. If legit paths are rejected, verify no symlinks/normalization surprises between baseDir and the trace directory

Example fix

// before
new TraceFileWriter(baseDir, userInputId, userInputDate); // untrusted
// after
if (!jobId.matches(UUID_REGEX) && !date.matches("\\d{4}-\\d{2}-\\d{2}")) {
    throw new IllegalArgumentException("untrusted trace path components");
}
new TraceFileWriter(baseDir, jobId, date);
Defensive patterns

Strategy: validation

Validate before calling

Path base = Paths.get(baseDir).toAbsolutePath().normalize();
Path resolved = base.resolve(Paths.get("traces", jobId, date)).normalize();
if (!resolved.startsWith(base)) throw new IllegalArgumentException("path escapes baseDir");

Type guard

boolean isInsideBase(Path base, Path p) { return p.normalize().startsWith(base); }

Try / catch

try { writer = new TraceFileWriter(baseDir, jobId, date); } catch (IllegalArgumentException e) { if (e.getMessage().contains("escapes baseDir")) { log.error("possible path traversal attempt", e); } else { throw e; } }

Prevention

When it happens

Trigger: Constructing TraceFileWriter with a jobId or date containing path-traversal sequences (../) that survive normalization; baseDir itself misconfigured to a path where resolve escapes containment.

Common situations: External input (jobId/date from request or config) used directly in the writer; symlinked or oddly-shaped baseDir setups; custom event reporters passing untrusted identifiers.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/3c8f86186dee6ffa. Report an issue: GitHub.