From ee10c9c33f0a594e3ff74e13c9344f14c68905a5 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:54:10 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITICAL]?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=20=ED=99=95=EC=9E=A5=EC=9E=90=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=9A=B0=ED=9A=8C=20=EC=B7=A8=EC=95=BD=EC=A0=90(Nu?= =?UTF-8?q?ll=20Byte)=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 Severity: CRITICAL πŸ’‘ Vulnerability: 파일 μ—…λ‘œλ“œ μ‹œ 파일λͺ…에 Null Byte(\u0000)λ₯Ό ν¬ν•¨ν•˜μ—¬ ν™•μž₯자 검증을 μš°νšŒν•  수 μžˆλŠ” 취약점이 μ‘΄μž¬ν–ˆμŠ΅λ‹ˆλ‹€. 🎯 Impact: μ°¨λ‹¨λœ ν™•μž₯자(예: .hwp, .hwpx)λ₯Ό κ°€μ§„ 파일이 ν—ˆμš©λœ ν™•μž₯자(예: .pdf)둜 μœ„μž₯λ˜μ–΄ μ‹œμŠ€ν…œμ— μ—…λ‘œλ“œλ˜κ³  처리될 수 μžˆμŠ΅λ‹ˆλ‹€. πŸ”§ Fix: `DefaultDocumentValidationService.java`의 `validateOrThrow` λ©”μ„œλ“œ μ΄ˆκΈ°μ— 파일λͺ…에 Null Byteκ°€ ν¬ν•¨λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•˜κ³ , 발견될 경우 μ¦‰μ‹œ `IllegalArgumentException`을 λ°œμƒμ‹œμΌœ μž…λ ₯을 κ±°λΆ€ν•˜λ„λ‘ μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€. βœ… Verification: Null Byteκ°€ ν¬ν•¨λœ 파일λͺ…을 ν…ŒμŠ€νŠΈν•˜λŠ” λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό `DefaultDocumentValidationServiceTest.java`에 μΆ”κ°€ν•˜μ—¬ 검증을 μ™„λ£Œν–ˆμœΌλ©°, `mvn test` 및 `mvn -DskipTests checkstyle:check`λ₯Ό λͺ¨λ‘ ν†΅κ³Όν–ˆμŠ΅λ‹ˆλ‹€. --- .jules/sentinel.md | 4 ++++ .../DefaultDocumentValidationService.java | 6 ++++++ .../DefaultDocumentValidationServiceTest.java | 16 ++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 76a97425..251c2fb5 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -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. diff --git a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java index b898f7ac..61a704a6 100644 --- a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java +++ b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java @@ -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."); diff --git a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java index dc1df793..1410964e 100644 --- a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java +++ b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java @@ -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()); + } }