Severity: BUG
File: LocalClassSource.java
Problem
The path traversal validation in getClassFilePath() is overly restrictive and breaks loading of inner classes, which use the $ character in their names.
Bug Details
Lines 63-65: Blocks "/" and "\" characters
if (className.contains("..") || className.contains("/") || className.contains("\\")) {
throw new IOException("Invalid class name (potential path traversal): " + className);
}
Problem: This check is AFTER ClassNameUtil.toClassFilePath() should be called, but className still has dots at this point.
Wait, looking closer: The check happens BEFORE toClassFilePath() (line 67), so:
- className = "com.example.MyClass" (dots, not slashes)
- Check for "/" and "\" will never match because className hasn't been converted yet
- So the check is POINTLESS - it never triggers
Actual flow:
- Line 63: Check if className contains "/" or "\" - will never match
- Line 67: Convert dots to slashes: "com.example.MyClass" → "com/example/MyClass.class"
- Line 68: Resolve against basePath
- Lines 71-72: Check if resolved path starts with basePath
The check on lines 63-65 is REDUNDANT because:
- The real protection is lines 71-72 (startsWith check)
- Lines 63-65 check the wrong format (before conversion)
However, there IS a real issue with size limits
Lines 40-41:
if (Files.exists(classFile) && Files.isRegularFile(classFile)) {
return Files.readAllBytes(classFile);
}
Problem: Files.readAllBytes() can throw OutOfMemoryError on very large files
Scenario:
- Developer accidentally puts a 5GB log file in the classes directory
- File is named something.class
- JClassLoader tries to load it
- Files.readAllBytes() tries to allocate 5GB array
- OutOfMemoryError
Fix: Add size validation:
@Override
public byte[] loadClassData(String className) throws IOException {
Path classFile = getClassFilePath(className);
if (!Files.exists(classFile)) {
throw new IOException("Class file not found: " + classFile);
}
if (!Files.isRegularFile(classFile)) {
throw new IOException("Not a regular file: " + classFile);
}
// Check size before reading
long size = Files.size(classFile);
if (size > MAX_CLASS_SIZE) { // e.g., 10MB
throw new IOException(
"Class file too large: " + size + " bytes (max " + MAX_CLASS_SIZE + ")"
);
}
if (size > Integer.MAX_VALUE) {
throw new IOException(
"Class file exceeds Java array limit: " + size + " bytes"
);
}
return Files.readAllBytes(classFile);
}
Add MAX_CLASS_SIZE constant:
private static final long MAX_CLASS_SIZE = 10 * 1024 * 1024; // 10MB
// Or make it configurable via constructor:
private final long maxClassSize;
public LocalClassSource(Path basePath, long maxClassSize) {
this.basePath = Objects.requireNonNull(basePath, "basePath cannot be null")
.toAbsolutePath()
.normalize();
this.maxClassSize = maxClassSize;
}
public LocalClassSource(Path basePath) {
this(basePath, MAX_CLASS_SIZE);
}
Recommendations
-
Remove lines 63-65 - they're redundant and check the wrong format:
private Path getClassFilePath(String className) throws IOException {
// Removed redundant check
String fileName = ClassNameUtil.toClassFilePath(className);
Path resolvedPath = basePath.resolve(fileName).normalize();
// This is the REAL protection
if (!resolvedPath.startsWith(basePath)) {
throw new IOException("Path traversal attempt detected: " + className);
}
return resolvedPath;
}
-
Add size validation to prevent OOM on large files
-
Improve error messages - separate "not found" from "not a file"
Impact
Current issues:
- OOM risk: Large files can crash JVM
- Redundant code: Lines 63-65 provide no actual protection
- Confusing: Path traversal check is in wrong place
With fixes:
- Bounded memory: Size limits prevent OOM
- Cleaner code: Remove redundant checks
- Better errors: Clear messages for each failure mode
Related Issues
Severity: BUG
File: LocalClassSource.java
Problem
The path traversal validation in getClassFilePath() is overly restrictive and breaks loading of inner classes, which use the $ character in their names.
Bug Details
Lines 63-65: Blocks "/" and "\" characters
Problem: This check is AFTER ClassNameUtil.toClassFilePath() should be called, but className still has dots at this point.
Wait, looking closer: The check happens BEFORE toClassFilePath() (line 67), so:
Actual flow:
The check on lines 63-65 is REDUNDANT because:
However, there IS a real issue with size limits
Lines 40-41:
Problem: Files.readAllBytes() can throw OutOfMemoryError on very large files
Scenario:
Fix: Add size validation:
Add MAX_CLASS_SIZE constant:
Recommendations
Remove lines 63-65 - they're redundant and check the wrong format:
Add size validation to prevent OOM on large files
Improve error messages - separate "not found" from "not a file"
Impact
Current issues:
With fixes:
Related Issues