Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 17b2c04

Browse files
security(GraphAnnotationManager): pre-read file size check to prevent OOM (CWE-400)
importFromFile() previously read the entire file into a StringBuilder before importFromJson()'s character-count guard could fire. A multi-GB adversarial file would exhaust the JVM heap before the size limit was ever checked. Fix: - Check file.length() against MAX_IMPORT_SIZE BEFORE reading - Add belt-and-suspenders character count check during read loop - Pre-size StringBuilder from file.length() to avoid reallocation - Remove incorrect ExportUtils.validateOutputPath() call on an input file (it's a read operation, not a write — the output-path validator was incorrectly restricting which files could be imported)
1 parent e322996 commit 17b2c04

1 file changed

Lines changed: 20 additions & 4 deletions

File tree

Gvisual/src/gvisual/GraphAnnotationManager.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -518,24 +518,40 @@ public void exportToFile(String filePath) throws IOException {
518518
* Import annotations from a file.
519519
*
520520
* <p>Validates that the file exists and is a regular file (not a
521-
* directory or special device). The file content is subject to
522-
* the same {@value #MAX_IMPORT_SIZE} character limit as
521+
* directory or special device). The file size is checked
522+
* <em>before</em> reading to prevent out-of-memory on adversarial
523+
* input (CWE-400). The file content is then subject to the same
524+
* {@value #MAX_IMPORT_SIZE} character limit as
523525
* {@link #importFromJson(String)}.</p>
524526
*/
525527
public int importFromFile(String filePath) throws IOException {
526528
java.io.File file = new java.io.File(filePath);
527-
ExportUtils.validateOutputPath(file);
528529
if (!file.exists()) {
529530
throw new java.io.FileNotFoundException("File not found: " + filePath);
530531
}
531532
if (!file.isFile()) {
532533
throw new IOException("Not a regular file: " + filePath);
533534
}
534-
StringBuilder sb = new StringBuilder();
535+
// Check file size BEFORE reading to prevent OOM on oversized input.
536+
// Previously the entire file was read into memory before the size
537+
// check in importFromJson() fired — a multi-GB file would exhaust
538+
// the heap before the guard ever triggered (CWE-400).
539+
if (file.length() > MAX_IMPORT_SIZE) {
540+
throw new IllegalArgumentException(
541+
"Annotation file exceeds maximum allowed size of "
542+
+ MAX_IMPORT_SIZE + " bytes: " + file.length() + " bytes");
543+
}
544+
StringBuilder sb = new StringBuilder((int) Math.min(file.length(), MAX_IMPORT_SIZE));
535545
try (BufferedReader r = new BufferedReader(new FileReader(file))) {
536546
String line;
537547
while ((line = r.readLine()) != null) {
538548
sb.append(line).append("\n");
549+
// Belt-and-suspenders: stop reading if we somehow exceed the limit
550+
if (sb.length() > MAX_IMPORT_SIZE) {
551+
throw new IllegalArgumentException(
552+
"Annotation file content exceeds maximum allowed size of "
553+
+ MAX_IMPORT_SIZE + " characters");
554+
}
539555
}
540556
}
541557
return importFromJson(sb.toString());

0 commit comments

Comments
 (0)