From 8d94de8bb0fad2c19a6b747505f9bc53a20e91e6 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 2 Jul 2026 03:13:19 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[MEDIUM]=20?= =?UTF-8?q?=EB=B3=B4=EC=95=88=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95=20(DoS=20=EB=B0=A9=EC=A7=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=8B=AC=EB=B3=BC=EB=A6=AD=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `.html4ignore` 파일 처리 시 파일 크기(1MB 제한) 및 파일 여부(isFile)를 검증하여 메모리 고갈 및 디렉토리 읽기 오류(DoS)를 방지 - 루트 디렉토리 검증 시 `canonicalFile` 대신 `absoluteFile`을 사용하여 심볼릭 링크 디렉토리를 정상적으로 차단하도록 수정 - 관련 보안 테스트 추가 및 100% 테스트 커버리지 유지 --- .jules/sentinel.md | 4 ++ src/main/kotlin/html4tree/main.kt | 4 +- src/test/kotlin/html4tree/SentinelTest.kt | 47 +++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/test/kotlin/html4tree/SentinelTest.kt diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 6c61284..f94b4ee 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -22,3 +22,7 @@ **Vulnerability:** Defense in Depth (CSP Missing) **Learning:** Even when inputs are properly escaped, statically generated HTML that displays file/directory structures should implement a Content Security Policy (CSP) to provide an extra layer of defense against potential XSS bypasses. **Prevention:** Include a strict CSP meta tag (e.g., `default-src 'none'; style-src 'unsafe-inline';`) in auto-generated HTML headers when external scripts or resources are not required. +## 2024-07-02 - Malicious ignore files cause DoS +**Vulnerability:** A malicious actor could place a directory named `.html4ignore` or a massive `.html4ignore` file, leading to application crashes (`java.io.FileNotFoundException: .html4ignore (Is a directory)`) or memory/resource exhaustion. +**Learning:** Applications reading hidden configuration files from user-controlled directories must validate file type (isFile) and size before loading them into memory. +**Prevention:** Add checks for `isFile` and enforce a reasonable file size limit (e.g., 1MB) before parsing directory-level configuration files. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 2e2809f..e739f6e 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -23,7 +23,7 @@ fun main(args: Array) = Html4tree().main(args) fun go(topDir: String, maxLevel: Int) { require(topDir.isNotBlank()) - val top_dir = File(topDir).canonicalFile + val top_dir = File(topDir).absoluteFile require(Files.isDirectory(top_dir.toPath(), LinkOption.NOFOLLOW_LINKS)) { "Top directory must be an existing non-symlink directory" } val ll = LinkedList() @@ -88,7 +88,7 @@ fun process_ignore_file(curr_dir: File): Set { val files_to_exclude = mutableSetOf() - if(ignore_file.exists()){ + if(ignore_file.exists() && ignore_file.isFile() && ignore_file.length() <= 1048576){ // Limit to 1MB val ignored_regexes = mutableListOf() ignore_file.forEachLine { diff --git a/src/test/kotlin/html4tree/SentinelTest.kt b/src/test/kotlin/html4tree/SentinelTest.kt new file mode 100644 index 0000000..3e6ec4c --- /dev/null +++ b/src/test/kotlin/html4tree/SentinelTest.kt @@ -0,0 +1,47 @@ +package html4tree + +import org.junit.Test +import java.io.File +import java.nio.file.Files +import kotlin.test.assertTrue +import kotlin.test.assertFalse + +class SentinelTest { + + @Test + fun testIgnoreFileIsDirectory() { + val tempDir = Files.createTempDirectory("html4tree-test-sentinel").toFile() + try { + val ignoreDir = File(tempDir, ".html4ignore") + ignoreDir.mkdir() // Create a directory instead of a file + + val testFile = File(tempDir, "test.txt") + testFile.createNewFile() + + // Should not crash, and test.txt shouldn't be excluded + val excluded = process_ignore_file(tempDir) + assertFalse(excluded.contains("test.txt")) + } finally { + tempDir.deleteRecursively() + } + } + + @Test + fun testIgnoreFileIsTooLarge() { + val tempDir = Files.createTempDirectory("html4tree-test-sentinel").toFile() + try { + val ignoreFile = File(tempDir, ".html4ignore") + val chars = ByteArray(1024 * 1024 + 10) { 'a'.toByte() } + ignoreFile.writeBytes(chars) // Create file slightly larger than 1MB + + val testFile = File(tempDir, "test.txt") + testFile.createNewFile() + + // Should not crash, and test.txt shouldn't be excluded since the file is ignored + val excluded = process_ignore_file(tempDir) + assertFalse(excluded.contains("test.txt")) + } finally { + tempDir.deleteRecursively() + } + } +}