Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
**Vulnerability:** Untrusted paths from API responses were directly assigned to `a.href` and used in `iframe` generation, which allows execution of malicious URIs like `javascript:` or `data:`.
**Learning:** Even when avoiding `innerHTML`, directly setting URL-like strings to DOM attributes without protocol validation introduces XSS vectors. The payload can be executed when the link is clicked or the iframe is loaded.
**Prevention:** Implement an `isSafeUrl` verification function to ensure the protocol is strictly `http:` or `https:` (using `new URL()`) before assigning untrusted inputs to DOM attributes like `href` or `src`.
## 2026-07-06 - Prevent Truncation Bypass Attacks via Null Bytes
**Vulnerability:** File validation logic (`DefaultDocumentValidationService.java`) failed to properly check for null bytes (`\u0000`) in filenames, relying on a naive replace/sanitize step in a different method that didn't prevent an attacker from bypassing the extension check.
**Learning:** Never silently sanitize or truncate input when performing security validations like file extensions. Attackers can use null bytes (`.hwp\0.pdf`) to trick the extension check (`.pdf`) while the underlying OS or downstream services process the truncated file (`.hwp`).
**Prevention:** Explicitly reject any input containing a null byte by throwing an exception immediately. Do not attempt to strip or sanitize it.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ public void validateOrThrow(MultipartFile file, PolicyOverrideRequest overrideRe
}

String fileName = file.getOriginalFilename();
if (fileName != null && fileName.indexOf('\0') != -1) {
throw new IllegalArgumentException(
"Filename cannot contain null bytes."
);
}

String extension = extensionOf(fileName);
if (extension.isEmpty()) {
throw new IllegalArgumentException("File extension is required.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,4 +401,20 @@ void throwsWhenSha256DigestIsUnavailableForOverrideAuditFingerprint() {
}
}
}

@Test
void rejectsFilenameWithNullByte() {
ConversionProperties conversionProperties = new ConversionProperties();
conversionProperties.setBlockedExtensions(Set.of("hwp", "hwpx"));
DefaultDocumentValidationService validationService = new DefaultDocumentValidationService(conversionProperties);

IllegalArgumentException ex = assertThrows(
IllegalArgumentException.class,
() -> validationService.validateOrThrow(
new MockMultipartFile("file", "contract.hwp\0.pdf", "application/octet-stream", new byte[] {1})
)
);

assertEquals("Filename cannot contain null bytes.", ex.getMessage());
}
}
Loading