1313 * This is the fastest class loading source as it reads directly from disk.
1414 */
1515public class LocalClassSource implements ClassSource {
16+ private static final long MAX_CLASS_SIZE = 10 * 1024 * 1024 ; // 10MB default max size
1617 private final Path basePath ;
18+ private final long maxClassSize ;
1719
1820 /**
19- * Creates a local class source with the specified base path.
21+ * Creates a local class source with the specified base path and max class size .
2022 *
2123 * @param basePath The base directory path containing class files
24+ * @param maxClassSize Maximum size of a class file in bytes
2225 * @throws NullPointerException if basePath is null
26+ * @throws IllegalArgumentException if maxClassSize is not positive
2327 */
24- public LocalClassSource (Path basePath ) {
28+ public LocalClassSource (Path basePath , long maxClassSize ) {
2529 this .basePath = Objects .requireNonNull (basePath , "basePath cannot be null" ).toAbsolutePath ().normalize ();
30+ if (maxClassSize <= 0 ) {
31+ throw new IllegalArgumentException ("maxClassSize must be positive" );
32+ }
33+ this .maxClassSize = maxClassSize ;
34+ }
35+
36+ /**
37+ * Creates a local class source with the specified base path and default max size (10MB).
38+ *
39+ * @param basePath The base directory path containing class files
40+ * @throws NullPointerException if basePath is null
41+ */
42+ public LocalClassSource (Path basePath ) {
43+ this (basePath , MAX_CLASS_SIZE );
2644 }
2745
2846 /**
29- * Creates a local class source with the specified base path string.
47+ * Creates a local class source with the specified base path string and default max size (10MB) .
3048 *
3149 * @param basePath The base directory path string containing class files
3250 */
@@ -37,10 +55,30 @@ public LocalClassSource(String basePath) {
3755 @ Override
3856 public byte [] loadClassData (String className ) throws IOException {
3957 Path classFile = getClassFilePath (className );
40- if (Files .exists (classFile ) && Files .isRegularFile (classFile )) {
41- return Files .readAllBytes (classFile );
58+
59+ if (!Files .exists (classFile )) {
60+ throw new IOException ("Class file not found: " + classFile );
61+ }
62+
63+ if (!Files .isRegularFile (classFile )) {
64+ throw new IOException ("Not a regular file: " + classFile );
65+ }
66+
67+ // Check size before reading to prevent OOM on large files
68+ long size = Files .size (classFile );
69+ if (size > maxClassSize ) {
70+ throw new IOException (
71+ "Class file too large: " + size + " bytes (max " + maxClassSize + ")"
72+ );
4273 }
43- throw new IOException ("Class file not found: " + classFile );
74+
75+ if (size > Integer .MAX_VALUE ) {
76+ throw new IOException (
77+ "Class file exceeds Java array limit: " + size + " bytes"
78+ );
79+ }
80+
81+ return Files .readAllBytes (classFile );
4482 }
4583
4684 @ Override
@@ -59,15 +97,11 @@ public String getDescription() {
5997 }
6098
6199 private Path getClassFilePath (String className ) throws IOException {
62- // Prevent path traversal attacks
63- if (className .contains (".." ) || className .contains ("/" ) || className .contains ("\\ " )) {
64- throw new IOException ("Invalid class name (potential path traversal): " + className );
65- }
66-
100+ // Convert class name to file path
67101 String fileName = ClassNameUtil .toClassFilePath (className );
68102 Path resolvedPath = basePath .resolve (fileName ).normalize ();
69103
70- // Ensure the resolved path is within basePath
104+ // Ensure the resolved path is within basePath (this is the real protection)
71105 if (!resolvedPath .startsWith (basePath )) {
72106 throw new IOException ("Path traversal attempt detected: " + className );
73107 }
0 commit comments