From ae0a7ee793bd9bcb8233494622430889b58559af Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 24 Sep 2026 20:52:41 +0000 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20=EB=B3=B4=EC=95=88=20=EC=A0=95=EC=B1=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC(.html4ignore)=20Fail-closed=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 심각도: CRITICAL πŸ’‘ 취약점: κΈ°μ‘΄μ—λŠ” `.html4ignore` 파일이 μ‘΄μž¬ν•˜λ”λΌλ„ κΆŒν•œ 문제둜 읽을 수 μ—†κ±°λ‚˜ TOCTOU 레이슀 μ»¨λ””μ…˜μœΌλ‘œ 인해 접근이 λΆˆκ°€λŠ₯ν•œ 경우, 이λ₯Ό λ¬΄μ‹œν•˜κ³ (fail-open) λ―Όκ°ν•œ νŒŒμΌλ“€μ΄ μΈλ±μ‹±λ˜λŠ” 취약점이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 🎯 영ν–₯: μ œν•œλ˜μ–΄μ•Ό ν•  λ―Όκ°ν•œ 정보(μ†ŒμŠ€μ½”λ“œ, λ°±μ—… 파일 λ“±)κ°€ 고의적인 파일 κΆŒν•œ μ‘°μž‘μ΄λ‚˜ 심볼릭 링크 곡격을 톡해 λŒ€μ€‘μ—κ²Œ λ…ΈμΆœλ  μœ„ν—˜μ΄ μžˆμŠ΅λ‹ˆλ‹€. (Information Exposure) πŸ”§ μˆ˜μ •μ‚¬ν•­: - `IgnoreFileReadException`을 λ„μž…ν•˜μ—¬ 파일 읽기 μ‹€νŒ¨ μ‹œ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ€‘λ‹¨μ‹œν‚€λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. - `.html4ignore` 파일이 디렉토리 λͺ©λ‘(snapshot)에 μ‘΄μž¬ν•˜μ§€λ§Œ 읽을 수 μ—†λŠ” 경우 μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ Fail-closed 정책을 κ°•μ œν•©λ‹ˆλ‹€. βœ… 검증: `./gradlew clean test` λͺ…λ Ήμ–΄λ₯Ό 톡해 λ³΄μ•ˆ μ •μ±… ν…ŒμŠ€νŠΈ 및 100% Jacoco 컀버리지 μš”κ±΄μ„ μΆ©μ‘±ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. --- .jules/sentinel.md | 4 ++ src/main/kotlin/html4tree/main.kt | 51 +++++++++++++++-------- src/test/kotlin/html4tree/MainTest.kt | 60 ++++++++++++++++++++++----- 3 files changed, 86 insertions(+), 29 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index a885865d..2efe1b50 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -99,3 +99,7 @@ **Root cause:** The protected implementation added canonical names to the exclusion set but did not compare each observed directory entry through a locale-stable normalized key. **Prevention:** Build one `Locale.ROOT` lowercase set from the canonical sensitive names, compare every observed name against it, and add the original spelling to the exclusion set so downstream exact membership remains correct. **Evidence:** `testProcessIgnoreFileTreatsSensitiveNamesCaseInsensitively` failed on test-only commit `472b916cd40f70693c4e1eb48956042a25353feb` (CI run `31469596932`) and passed with the source fix at `bb113d858ccfc42ddaecf6729749b238e5ade2d0` (CI run `31469921661`). +## 2024-09-24 - [CRITICAL] Fail-closed Security Policy for .html4ignore Files +**Vulnerability:** The application failed to properly enforce security policies defined in `.html4ignore`. If the file was unreadable due to restrictive permissions or due to a TOCTOU race condition after enumeration, the system failed-open. This could lead to information exposure by indexing and publishing directories containing sensitive files that were intended to be ignored. +**Learning:** Checking for `.html4ignore` existence without verifying read access and intentionally failing if access is denied violates the principle of failing securely. Security controls must enforce a fail-closed behavior to avoid bypassing intended protections. +**Prevention:** Always employ fail-closed logic when evaluating security boundaries or access control lists (like `.html4ignore`). Ensure that unreadable policy files throw a dedicated exception (`IgnoreFileReadException`) to stop the operation, instead of silently continuing. Also, avoid TOCTOU by ensuring the presence of the policy file from the directory snapshot is strictly verified and safely read. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 0972fa2c..c468eb64 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -200,20 +200,26 @@ internal fun crawl_directories( val dirFilesNames = dirFiles?.let { files -> Array(files.size) { index -> files[index].name } } - val exclude = processIgnoreFile(lle.file, dirFilesNames) - - if(maxLevel == -1 || currentLevel <= maxLevel) - processDirectory(lle.file, exclude, dirFiles) - - if(maxLevel == -1 || currentLevel < maxLevel) { - dirFiles?.forEach { - // ⚑ Bolt Performance Optimization: Short-circuit OS stat calls - // by checking cheap in-memory string exclusion rules first - if(!it.name.isHiddenFile() && it.name !in exclude) { - val childAttrs = readAttributes(it) - if(childAttrs != null && childAttrs.isDirectory && !childAttrs.isSymbolicLink) { - val childEntry = LinkedListEntry(it, currentLevel+1, readIdentity(it).key) - ll.push(childEntry) + val exclude = try { + processIgnoreFile(lle.file, dirFilesNames) + } catch (e: IgnoreFileReadException) { + null + } + + if(exclude != null) { + if(maxLevel == -1 || currentLevel <= maxLevel) + processDirectory(lle.file, exclude, dirFiles) + + if(maxLevel == -1 || currentLevel < maxLevel) { + dirFiles?.forEach { + // ⚑ Bolt Performance Optimization: Short-circuit OS stat calls + // by checking cheap in-memory string exclusion rules first + if(!it.name.isHiddenFile() && it.name !in exclude) { + val childAttrs = readAttributes(it) + if(childAttrs != null && childAttrs.isDirectory && !childAttrs.isSymbolicLink) { + val childEntry = LinkedListEntry(it, currentLevel+1, readIdentity(it).key) + ll.push(childEntry) + } } } } @@ -293,6 +299,12 @@ fun String.urlEncodePath(): String { return encoded?.toString() ?: this } +/** + * Exception thrown when a policy file like .html4ignore exists but cannot be safely read. + * Enforces a fail-closed security contract to prevent TOCTOU bypasses and information exposure. + */ +class IgnoreFileReadException(message: String, cause: Throwable? = null) : RuntimeException(message, cause) + fun process_ignore_file(curr_dir: File, dirFilesNames: Array? = null): Set { val ignore_filename = ".html4ignore" @@ -303,10 +315,13 @@ fun process_ignore_file(curr_dir: File, dirFilesNames: Array? = null): S val files_to_exclude = mutableSetOf() - // λ³΄μ•ˆ ν–₯상: .html4ignore 파일이 일반 νŒŒμΌμΈμ§€ ν™•μΈν•˜κ³ , 심볼릭 링크인 경우 λ¬΄μ‹œν•˜μ—¬ DoS 및 경둜 μ‘°μž‘μ„ λ°©μ§€ν•©λ‹ˆλ‹€. - // λ³΄μ•ˆ ν–₯상: 파일 크기(1MB μ œν•œ) 및 쀄 수(1000쀄), μ •κ·œμ‹ 길이(100자)λ₯Ό μ œν•œν•˜μ—¬ ReDoS 및 λ©”λͺ¨λ¦¬ 고갈(OOM) λ°©μ§€ - // λ³΄μ•ˆ ν–₯상: κΆŒν•œμ΄ μ—†λŠ” 파일 μ ‘κ·Ό μ‹œ λ°œμƒν•˜λŠ” μ˜ˆμ™Έ(DoS)λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ canRead() μΆ”κ°€ 확인 - if(ignore_file.isFile && !Files.isSymbolicLink(ignore_file.toPath()) && ignore_file.canRead() && ignore_file.length() <= 1048576){ + // λ³΄μ•ˆ ν–₯상: TOCTOU(Time-of-check to time-of-use) 취약점 λ°©μ§€ 및 Fail-closed μ •μ±… 적용 + val hasIgnoreFile = (dirFilesNames != null && dirFilesNames.contains(ignore_filename)) || ignore_file.exists() || Files.isSymbolicLink(ignore_file.toPath()) + + if(hasIgnoreFile){ + if(!ignore_file.isFile || Files.isSymbolicLink(ignore_file.toPath()) || !ignore_file.canRead() || ignore_file.length() > 1048576){ + throw IgnoreFileReadException("Policy file cannot be safely read: fail closed.") + } val ignored_matchers = mutableListOf() ignore_file.useLines { lines -> diff --git a/src/test/kotlin/html4tree/MainTest.kt b/src/test/kotlin/html4tree/MainTest.kt index 5b76cc5d..f983aa1c 100644 --- a/src/test/kotlin/html4tree/MainTest.kt +++ b/src/test/kotlin/html4tree/MainTest.kt @@ -718,9 +718,13 @@ class MainTest { val ignoreDir = File(tempDir, ".html4ignore") ignoreDir.mkdir() - // This should not crash or parse the directory - val excluded = process_ignore_file(tempDir, null) - assertTrue(excluded.contains("index.html")) + var thrown = false + try { + process_ignore_file(tempDir, null) + } catch (e: IgnoreFileReadException) { + thrown = true + } + assertTrue(thrown) } @Test @@ -762,10 +766,13 @@ class MainTest { File(tempDir, "test.txt").createNewFile() - // Should ignore the symlink and NOT parse it - val excluded = process_ignore_file(tempDir, null) - assertFalse(excluded.contains("test.txt")) - assertTrue(excluded.contains("index.html")) + var thrown = false + try { + process_ignore_file(tempDir, null) + } catch (e: IgnoreFileReadException) { + thrown = true + } + assertTrue(thrown) } @Test @@ -777,10 +784,13 @@ class MainTest { File(tempDir, "test.txt").createNewFile() - // Should ignore the file because it's too large - val excluded = process_ignore_file(tempDir, null) - assertFalse(excluded.contains("test.txt")) - assertTrue(excluded.contains("index.html")) + var thrown = false + try { + process_ignore_file(tempDir, null) + } catch (e: IgnoreFileReadException) { + thrown = true + } + assertTrue(thrown) } @Test @@ -946,4 +956,32 @@ class MainTest { assertTrue(content.contains("

Root

")) } + @Test + fun testCrawlDirectoriesThrowsIgnoreFileReadException() { + val subdir = File(tempDir, "ignore_exception_test") + subdir.mkdir() + val ll = LinkedList() + val entry = LinkedListEntry(subdir, 0) + entry.fileKey = "test-key" + ll.push(entry) + + var processed = false + var listed = false + + crawl_directories( + ll, + -1, + processDirectory = { _, _, _ -> processed = true }, + processIgnoreFile = { _, _ -> throw IgnoreFileReadException("mock") }, + listFiles = { + listed = true + emptyArray() + }, + readAttributes = { _ -> createMockAttributes(isDir = true, isSymlink = false) }, + readIdentity = { FileIdentity("test-key", true) } + ) + + assertFalse(processed, "Directory must not be processed if IgnoreFileReadException is thrown") + } + } From 5dd7daa99c5324aa27074cd3dd2173ef74ba4ce9 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:55:07 +0000 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20=EB=B3=B4=EC=95=88=20=EC=A0=95=EC=B1=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC(.html4ignore)=20Fail-closed=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 심각도: CRITICAL πŸ’‘ 취약점: κΈ°μ‘΄μ—λŠ” `.html4ignore` 파일이 μ‘΄μž¬ν•˜λ”λΌλ„ κΆŒν•œ 문제둜 읽을 수 μ—†κ±°λ‚˜ TOCTOU 레이슀 μ»¨λ””μ…˜μœΌλ‘œ 인해 접근이 λΆˆκ°€λŠ₯ν•œ 경우, 이λ₯Ό λ¬΄μ‹œν•˜κ³ (fail-open) λ―Όκ°ν•œ νŒŒμΌλ“€μ΄ μΈλ±μ‹±λ˜λŠ” 취약점이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 🎯 영ν–₯: μ œν•œλ˜μ–΄μ•Ό ν•  λ―Όκ°ν•œ 정보(μ†ŒμŠ€μ½”λ“œ, λ°±μ—… 파일 λ“±)κ°€ 고의적인 파일 κΆŒν•œ μ‘°μž‘μ΄λ‚˜ 심볼릭 링크 곡격을 톡해 λŒ€μ€‘μ—κ²Œ λ…ΈμΆœλ  μœ„ν—˜μ΄ μžˆμŠ΅λ‹ˆλ‹€. (Information Exposure) πŸ”§ μˆ˜μ •μ‚¬ν•­: - `IgnoreFileReadException`을 λ„μž…ν•˜μ—¬ 파일 읽기 μ‹€νŒ¨ μ‹œ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ€‘λ‹¨μ‹œν‚€λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. - `.html4ignore` 파일이 디렉토리 λͺ©λ‘(snapshot)에 μ‘΄μž¬ν•˜μ§€λ§Œ 읽을 수 μ—†λŠ” 경우 μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ Fail-closed 정책을 κ°•μ œν•©λ‹ˆλ‹€. βœ… 검증: `./gradlew clean test` λͺ…λ Ήμ–΄λ₯Ό 톡해 λ³΄μ•ˆ μ •μ±… ν…ŒμŠ€νŠΈ 및 100% Jacoco 컀버리지 μš”κ±΄μ„ μΆ©μ‘±ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. From 374212111076831156a76289a75b8e83d7e33915 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 25 Sep 2026 16:31:57 +0000 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20=EB=B3=B4=EC=95=88=20=EC=A0=95=EC=B1=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC(.html4ignore)=20Fail-closed=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 심각도: CRITICAL πŸ’‘ 취약점: κΈ°μ‘΄μ—λŠ” `.html4ignore` 파일이 μ‘΄μž¬ν•˜λ”λΌλ„ κΆŒν•œ 문제둜 읽을 수 μ—†κ±°λ‚˜ TOCTOU 레이슀 μ»¨λ””μ…˜μœΌλ‘œ 인해 접근이 λΆˆκ°€λŠ₯ν•œ 경우, 이λ₯Ό λ¬΄μ‹œν•˜κ³ (fail-open) λ―Όκ°ν•œ νŒŒμΌλ“€μ΄ μΈλ±μ‹±λ˜λŠ” 취약점이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 🎯 영ν–₯: μ œν•œλ˜μ–΄μ•Ό ν•  λ―Όκ°ν•œ 정보(μ†ŒμŠ€μ½”λ“œ, λ°±μ—… 파일 λ“±)κ°€ 고의적인 파일 κΆŒν•œ μ‘°μž‘μ΄λ‚˜ 심볼릭 링크 곡격을 톡해 λŒ€μ€‘μ—κ²Œ λ…ΈμΆœλ  μœ„ν—˜μ΄ μžˆμŠ΅λ‹ˆλ‹€. (Information Exposure) πŸ”§ μˆ˜μ •μ‚¬ν•­: - `IgnoreFileReadException`을 λ„μž…ν•˜μ—¬ 파일 읽기 μ‹€νŒ¨ μ‹œ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ€‘λ‹¨μ‹œν‚€λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. - `.html4ignore` 파일이 디렉토리 λͺ©λ‘(snapshot)에 μ‘΄μž¬ν•˜μ§€λ§Œ 읽을 수 μ—†λŠ” 경우 μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ Fail-closed 정책을 κ°•μ œν•©λ‹ˆλ‹€. βœ… 검증: `./gradlew clean test` λͺ…λ Ήμ–΄λ₯Ό 톡해 λ³΄μ•ˆ μ •μ±… ν…ŒμŠ€νŠΈ 및 100% Jacoco 컀버리지 μš”κ±΄μ„ μΆ©μ‘±ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. From b40a17fdf7034f4c5d50b05adbd219c777b5fc27 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 25 Sep 2026 22:43:07 +0000 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20=EB=B3=B4=EC=95=88=20=EC=A0=95=EC=B1=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC(.html4ignore)=20Fail-closed=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 심각도: CRITICAL πŸ’‘ 취약점: κΈ°μ‘΄μ—λŠ” `.html4ignore` 파일이 μ‘΄μž¬ν•˜λ”λΌλ„ κΆŒν•œ 문제둜 읽을 수 μ—†κ±°λ‚˜ TOCTOU 레이슀 μ»¨λ””μ…˜μœΌλ‘œ 인해 접근이 λΆˆκ°€λŠ₯ν•œ 경우, 이λ₯Ό λ¬΄μ‹œν•˜κ³ (fail-open) λ―Όκ°ν•œ νŒŒμΌλ“€μ΄ μΈλ±μ‹±λ˜λŠ” 취약점이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 🎯 영ν–₯: μ œν•œλ˜μ–΄μ•Ό ν•  λ―Όκ°ν•œ 정보(μ†ŒμŠ€μ½”λ“œ, λ°±μ—… 파일 λ“±)κ°€ 고의적인 파일 κΆŒν•œ μ‘°μž‘μ΄λ‚˜ 심볼릭 링크 곡격을 톡해 λŒ€μ€‘μ—κ²Œ λ…ΈμΆœλ  μœ„ν—˜μ΄ μžˆμŠ΅λ‹ˆλ‹€. (Information Exposure) πŸ”§ μˆ˜μ •μ‚¬ν•­: - `IgnoreFileReadException`을 λ„μž…ν•˜μ—¬ 파일 읽기 μ‹€νŒ¨ μ‹œ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ€‘λ‹¨μ‹œν‚€λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. - `.html4ignore` 파일이 디렉토리 λͺ©λ‘(snapshot)에 μ‘΄μž¬ν•˜μ§€λ§Œ 읽을 수 μ—†λŠ” 경우 μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ Fail-closed 정책을 κ°•μ œν•©λ‹ˆλ‹€. βœ… 검증: `./gradlew clean test` λͺ…λ Ήμ–΄λ₯Ό 톡해 λ³΄μ•ˆ μ •μ±… ν…ŒμŠ€νŠΈ 및 100% Jacoco 컀버리지 μš”κ±΄μ„ μΆ©μ‘±ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. From 72f5eecad7e9635bc8f0d5bac298d1ab9c85ceca Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 03:28:17 +0000 Subject: [PATCH 5/8] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20=EB=B3=B4=EC=95=88=20=EC=A0=95=EC=B1=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC(.html4ignore)=20Fail-closed=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 심각도: CRITICAL πŸ’‘ 취약점: κΈ°μ‘΄μ—λŠ” `.html4ignore` 파일이 μ‘΄μž¬ν•˜λ”λΌλ„ κΆŒν•œ 문제둜 읽을 수 μ—†κ±°λ‚˜ TOCTOU 레이슀 μ»¨λ””μ…˜μœΌλ‘œ 인해 접근이 λΆˆκ°€λŠ₯ν•œ 경우, 이λ₯Ό λ¬΄μ‹œν•˜κ³ (fail-open) λ―Όκ°ν•œ νŒŒμΌλ“€μ΄ μΈλ±μ‹±λ˜λŠ” 취약점이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 🎯 영ν–₯: μ œν•œλ˜μ–΄μ•Ό ν•  λ―Όκ°ν•œ 정보(μ†ŒμŠ€μ½”λ“œ, λ°±μ—… 파일 λ“±)κ°€ 고의적인 파일 κΆŒν•œ μ‘°μž‘μ΄λ‚˜ 심볼릭 링크 곡격을 톡해 λŒ€μ€‘μ—κ²Œ λ…ΈμΆœλ  μœ„ν—˜μ΄ μžˆμŠ΅λ‹ˆλ‹€. (Information Exposure) πŸ”§ μˆ˜μ •μ‚¬ν•­: - `IgnoreFileReadException`을 λ„μž…ν•˜μ—¬ 파일 읽기 μ‹€νŒ¨ μ‹œ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ€‘λ‹¨μ‹œν‚€λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. - `.html4ignore` 파일이 디렉토리 λͺ©λ‘(snapshot)에 μ‘΄μž¬ν•˜μ§€λ§Œ 읽을 수 μ—†λŠ” 경우 μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ Fail-closed 정책을 κ°•μ œν•©λ‹ˆλ‹€. βœ… 검증: `./gradlew clean test` λͺ…λ Ήμ–΄λ₯Ό 톡해 λ³΄μ•ˆ μ •μ±… ν…ŒμŠ€νŠΈ 및 100% Jacoco 컀버리지 μš”κ±΄μ„ μΆ©μ‘±ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. From c9a8b2476c6fc39c85056d7d55e02a2b41d9bab2 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 08:31:28 +0000 Subject: [PATCH 6/8] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20=EB=B3=B4=EC=95=88=20=EC=A0=95=EC=B1=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC(.html4ignore)=20Fail-closed=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 심각도: CRITICAL πŸ’‘ 취약점: κΈ°μ‘΄μ—λŠ” `.html4ignore` 파일이 μ‘΄μž¬ν•˜λ”λΌλ„ κΆŒν•œ 문제둜 읽을 수 μ—†κ±°λ‚˜ TOCTOU 레이슀 μ»¨λ””μ…˜μœΌλ‘œ 인해 접근이 λΆˆκ°€λŠ₯ν•œ 경우, 이λ₯Ό λ¬΄μ‹œν•˜κ³ (fail-open) λ―Όκ°ν•œ νŒŒμΌλ“€μ΄ μΈλ±μ‹±λ˜λŠ” 취약점이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 🎯 영ν–₯: μ œν•œλ˜μ–΄μ•Ό ν•  λ―Όκ°ν•œ 정보(μ†ŒμŠ€μ½”λ“œ, λ°±μ—… 파일 λ“±)κ°€ 고의적인 파일 κΆŒν•œ μ‘°μž‘μ΄λ‚˜ 심볼릭 링크 곡격을 톡해 λŒ€μ€‘μ—κ²Œ λ…ΈμΆœλ  μœ„ν—˜μ΄ μžˆμŠ΅λ‹ˆλ‹€. (Information Exposure) πŸ”§ μˆ˜μ •μ‚¬ν•­: - `IgnoreFileReadException`을 λ„μž…ν•˜μ—¬ 파일 읽기 μ‹€νŒ¨ μ‹œ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ€‘λ‹¨μ‹œν‚€λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. - `.html4ignore` 파일이 디렉토리 λͺ©λ‘(snapshot)에 μ‘΄μž¬ν•˜μ§€λ§Œ 읽을 수 μ—†λŠ” 경우 μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ Fail-closed 정책을 κ°•μ œν•©λ‹ˆλ‹€. βœ… 검증: `./gradlew clean test` λͺ…λ Ήμ–΄λ₯Ό 톡해 λ³΄μ•ˆ μ •μ±… ν…ŒμŠ€νŠΈ 및 100% Jacoco 컀버리지 μš”κ±΄μ„ μΆ©μ‘±ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. From e97a7c70e55021d712f4af47ffe386ee6bdefe93 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:02:10 +0000 Subject: [PATCH 7/8] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20=EB=B3=B4=EC=95=88=20=EC=A0=95=EC=B1=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC(.html4ignore)=20Fail-closed=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 심각도: CRITICAL πŸ’‘ 취약점: κΈ°μ‘΄μ—λŠ” `.html4ignore` 파일이 μ‘΄μž¬ν•˜λ”λΌλ„ κΆŒν•œ 문제둜 읽을 수 μ—†κ±°λ‚˜ TOCTOU 레이슀 μ»¨λ””μ…˜μœΌλ‘œ 인해 접근이 λΆˆκ°€λŠ₯ν•œ 경우, 이λ₯Ό λ¬΄μ‹œν•˜κ³ (fail-open) λ―Όκ°ν•œ νŒŒμΌλ“€μ΄ μΈλ±μ‹±λ˜λŠ” 취약점이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 🎯 영ν–₯: μ œν•œλ˜μ–΄μ•Ό ν•  λ―Όκ°ν•œ 정보(μ†ŒμŠ€μ½”λ“œ, λ°±μ—… 파일 λ“±)κ°€ 고의적인 파일 κΆŒν•œ μ‘°μž‘μ΄λ‚˜ 심볼릭 링크 곡격을 톡해 λŒ€μ€‘μ—κ²Œ λ…ΈμΆœλ  μœ„ν—˜μ΄ μžˆμŠ΅λ‹ˆλ‹€. (Information Exposure) πŸ”§ μˆ˜μ •μ‚¬ν•­: - `IgnoreFileReadException`을 λ„μž…ν•˜μ—¬ 파일 읽기 μ‹€νŒ¨ μ‹œ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ€‘λ‹¨μ‹œν‚€λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. - `.html4ignore` 파일이 디렉토리 λͺ©λ‘(snapshot)에 μ‘΄μž¬ν•˜μ§€λ§Œ 읽을 수 μ—†λŠ” 경우 μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ Fail-closed 정책을 κ°•μ œν•©λ‹ˆλ‹€. βœ… 검증: `./gradlew clean test` λͺ…λ Ήμ–΄λ₯Ό 톡해 λ³΄μ•ˆ μ •μ±… ν…ŒμŠ€νŠΈ 및 100% Jacoco 컀버리지 μš”κ±΄μ„ μΆ©μ‘±ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. From 57bf2b650b0e5310d83a5e47d4ddc2fd86212a9c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 15:02:07 +0000 Subject: [PATCH 8/8] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20=EB=B3=B4=EC=95=88=20=EC=A0=95=EC=B1=85=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC(.html4ignore)=20Fail-closed=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨 심각도: CRITICAL πŸ’‘ 취약점: κΈ°μ‘΄μ—λŠ” `.html4ignore` 파일이 μ‘΄μž¬ν•˜λ”λΌλ„ κΆŒν•œ 문제둜 읽을 수 μ—†κ±°λ‚˜ TOCTOU 레이슀 μ»¨λ””μ…˜μœΌλ‘œ 인해 접근이 λΆˆκ°€λŠ₯ν•œ 경우, 이λ₯Ό λ¬΄μ‹œν•˜κ³ (fail-open) λ―Όκ°ν•œ νŒŒμΌλ“€μ΄ μΈλ±μ‹±λ˜λŠ” 취약점이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 🎯 영ν–₯: μ œν•œλ˜μ–΄μ•Ό ν•  λ―Όκ°ν•œ 정보(μ†ŒμŠ€μ½”λ“œ, λ°±μ—… 파일 λ“±)κ°€ 고의적인 파일 κΆŒν•œ μ‘°μž‘μ΄λ‚˜ 심볼릭 링크 곡격을 톡해 λŒ€μ€‘μ—κ²Œ λ…ΈμΆœλ  μœ„ν—˜μ΄ μžˆμŠ΅λ‹ˆλ‹€. (Information Exposure) πŸ”§ μˆ˜μ •μ‚¬ν•­: - `IgnoreFileReadException`을 λ„μž…ν•˜μ—¬ 파일 읽기 μ‹€νŒ¨ μ‹œ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ€‘λ‹¨μ‹œν‚€λ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. - `.html4ignore` 파일이 디렉토리 λͺ©λ‘(snapshot)에 μ‘΄μž¬ν•˜μ§€λ§Œ 읽을 수 μ—†λŠ” 경우 μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œμΌœ Fail-closed 정책을 κ°•μ œν•©λ‹ˆλ‹€. βœ… 검증: `./gradlew clean test` λͺ…λ Ήμ–΄λ₯Ό 톡해 λ³΄μ•ˆ μ •μ±… ν…ŒμŠ€νŠΈ 및 100% Jacoco 컀버리지 μš”κ±΄μ„ μΆ©μ‘±ν•˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€.