Skip to content
Open
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
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,8 @@
**Vulnerability:** CSP ν•΄μ‹œ 뢈일치둜 μΈν•œ 인라인 μŠ€νƒ€μΌ 차단
**Learning:** λΈŒλΌμš°μ €λŠ” 인라인 μŠ€ν¬λ¦½νŠΈμ™€ μŠ€νƒ€μΌμ˜ λ‚΄λΆ€ ν…μŠ€νŠΈ(곡백과 μ€„λ°”κΏˆ 포함)λ₯Ό μ •ν™•ν•˜κ²Œ ν•΄μ‹±ν•˜μ—¬ Content-Security-Policy(CSP) ν•΄μ‹œμ™€ λΉ„κ΅ν•©λ‹ˆλ‹€. Kotlin의 멀티라인 λ¬Έμžμ—΄(`"""`)을 μ‚¬μš©ν•˜μ—¬ ν…œν”Œλ¦Ώμ— μ½˜ν…μΈ λ₯Ό μ£Όμž…ν•  λ•Œ 암묡적인 μ—¬λ°±μ΄λ‚˜ μ€„λ°”κΏˆμ΄ μΆ”κ°€λ˜λ©΄ μ΅œμ’… HTML λ¬Έμžμ—΄μ΄ λ³€κ²½λ˜μ–΄ CSP ν•΄μ‹œκ°€ λ¬΄νš¨ν™”λ©λ‹ˆλ‹€.
**Prevention:** μ½˜ν…μΈ λ₯Ό ν•΄μ‹±ν•˜κΈ° 전에 `.trimIndent()`λ₯Ό μ μš©ν•˜μ—¬ 원본 λ¬Έμžμ—΄μ„ μ •κ·œν™”ν•˜κ³ , HTML ν…œν”Œλ¦Ώμ— μ£Όμž…ν•  λ•Œ `<style>${exactContent}</style>`와 같이 곡백 없이 μ£Όμž…ν•˜μ—¬ ν•΄μ‹œκ°€ μ™„λ²½ν•˜κ²Œ μΌμΉ˜ν•˜λ„λ‘ ν•΄μ•Ό ν•©λ‹ˆλ‹€.

## 2026-08-08 - [html4tree] 경둜 및 μž…λ ₯ μ˜ˆμ™Έ 처리 κ΄€λ ¨ λ³΄μ•ˆ κ°•ν™” (DoS 및 정보 λ…ΈμΆœ λ°©μ§€)
**Vulnerability:** `getPathMatcher`κ°€ 던질 수 μžˆλŠ” `IllegalArgumentException` λˆ„λ½μ— μ˜ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ν¬λž˜μ‹œ(DoS), 그리고 파일 μ‹œμŠ€ν…œ 루트(`""`) 처리 μ‹œ 빈 λ¬Έμžμ—΄ λ…ΈμΆœλ‘œ μΈν•œ μ ‘κ·Όμ„± 문제 및 λŒ€μ²΄ 경둜 μ‚¬μš© μœ λ„λ‘œ μΈν•œ 정보 λ…ΈμΆœ(Information Disclosure) μœ„ν—˜.
**Learning:** `getPathMatcher`와 같은 κΈ°λ³Έ λΌμ΄λΈŒλŸ¬λ¦¬λŠ” μ˜ˆμƒμΉ˜ λͺ»ν•œ μž…λ ₯에 λŒ€ν•΄ λ¬Έμ„œν™”λ˜μ§€ μ•Šμ€ λŸ°νƒ€μž„ μ˜ˆμ™Έλ₯Ό 던질 수 있으며, 파일λͺ… 기반 μ²˜λ¦¬λŠ” 루트 디렉토리 같은 μ—£μ§€ μΌ€μ΄μŠ€λ₯Ό κ°„κ³Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.
**Prevention:** μ‚¬μš©μž 제곡 νŒ¨ν„΄ 처리 μ‹œ `Exception`을 ν¬κ΄„μ μœΌλ‘œ μž‘μ•„ ν¬λž˜μ‹œλ₯Ό λ°©μ§€ν•˜κ³ , 빈 파일λͺ…에 λŒ€ν•΄ `"/"` 같은 μ•ˆμ „ν•œ 정적 λŒ€μ²΄κ°’μ„ μ œκ³΅ν•˜μ—¬ 파일 μ‹œμŠ€ν…œ μ ˆλŒ€ κ²½λ‘œκ°€ λ…ΈμΆœλ˜λŠ” 정보 λˆ„μΆœμ„ λ°©μ§€ν•˜μ‹­μ‹œμ˜€.
7 changes: 4 additions & 3 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ fun process_ignore_file(curr_dir: File, dirFilesNames: Array<String>? = null): S
if (pattern.isNotEmpty() && pattern.length <= 100) {
try {
ignored_matchers.add(java.nio.file.FileSystems.getDefault().getPathMatcher("glob:$pattern"))
} catch (_: java.util.regex.PatternSyntaxException) {
} catch (_: Exception) {
}
}
}
Expand Down Expand Up @@ -326,6 +326,7 @@ fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array

val exclude: Set<String> = excludeSet ?: process_ignore_file(curr_dir)

val dirName = curr_dir.name.ifEmpty { "/" }
val index_top = """<!doctype html>
<html lang="ko">
<head>
Expand All @@ -336,12 +337,12 @@ fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src '${STYLE_HASH}'; base-uri 'none'; form-action 'none';">
<!-- λ³΄μ•ˆ ν–₯상: 리퍼러λ₯Ό ν†΅ν•œ 디렉토리 경둜 λ…ΈμΆœ λ°©μ§€ -->
<meta name="referrer" content="no-referrer">
<title>${curr_dir.getName().escapeHtml()}</title>
<title>${dirName.escapeHtml()}</title>
<style>${CSS_CONTENT}</style>
</head>
<body>
<main>
<h1>${curr_dir.getName().escapeHtml()}</h1>
<h1>${dirName.escapeHtml()}</h1>
<nav aria-label="디렉토리 λͺ©λ‘">
<ul role="list">
<li><a class="dir-link" href="./.." aria-label="μƒμœ„ λ””λ ‰ν† λ¦¬λ‘œ 이동" title="μƒμœ„ λ””λ ‰ν† λ¦¬λ‘œ 이동"><span class="icon" aria-hidden="true">&#x21B0;</span> <span>..</span></a></li>
Expand Down
61 changes: 61 additions & 0 deletions src/test/kotlin/html4tree/GlobTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package html4tree

import org.junit.Test
import java.io.File
import java.nio.file.Files
import kotlin.test.assertTrue

class GlobTest {
@Test
fun testGlobExceptionHandled() {
val tempDir = Files.createTempDirectory("glob_test").toFile()
try {
val ignoreFile = File(tempDir, ".html4ignore")
ignoreFile.writeText("[\nvalid.txt\n")

val validFile = File(tempDir, "valid.txt")
validFile.createNewFile()

val excluded = process_ignore_file(tempDir, null)
assertTrue(excluded.contains("valid.txt"))
} finally {
tempDir.deleteRecursively()
}
}

@Test
fun testDirNameFallbackNormal() {
val tempDir = Files.createTempDirectory("fallback_test_normal").toFile()
try {
process_dir(tempDir, emptySet(), emptyArray())
val indexFile = File(tempDir, "index.html")
assertTrue(indexFile.exists())
val content = indexFile.readText()
val escapedDirName = tempDir.name.escapeHtml()
assertTrue(content.contains("<title>$escapedDirName</title>"))
assertTrue(content.contains("<h1>$escapedDirName</h1>"))
} finally {
tempDir.deleteRecursively()
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

class ExtraTest {
@Test
fun testFallbackInlinedEmpty() {
val tempDir = Files.createTempDirectory("fallback_test_empty").toFile()
val emptyNameDir = object : File(tempDir.path) {
override fun getName(): String = ""
}
try {
process_dir(emptyNameDir, emptySet(), emptyArray())
val indexFile = File(tempDir, "index.html")
assertTrue(indexFile.exists())
val content = indexFile.readText()
assertTrue(content.contains("<title>/</title>"))
assertTrue(content.contains("<h1>/</h1>"))
} finally {
tempDir.deleteRecursively()
}
}
}
Loading