Skip to content
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,7 @@
**Vulnerability:** 정적 HTML 생성 λ„κ΅¬μ—μ„œ 맀번 λ‹€λ₯Έ Nonceλ₯Ό λ™μ μœΌλ‘œ μƒμ„±ν•˜μ—¬ CSP에 μ μš©ν•˜λŠ” 것은, 캐싱 νš¨μœ¨μ„ μ €ν•˜μ‹œν‚¬ 뿐만 μ•„λ‹ˆλΌ 정적 배포 ν™˜κ²½(예: GitHub Pages λ“±)μ—μ„œ μ˜¬λ°”λ₯Έ λ³΄μ•ˆ μ •μ±… μˆ˜λ¦½μ„ λ°©ν•΄ν•  수 μžˆλŠ” μ•ˆν‹° νŒ¨ν„΄μž…λ‹ˆλ‹€.
**Learning:** μ •μ μœΌλ‘œ κ³ μ •λœ 인라인 μŠ€νƒ€μΌμ΄λ‚˜ μŠ€ν¬λ¦½νŠΈμ—λŠ” λ‚œμˆ˜ν™”λœ Nonce보닀 μ½˜ν…μΈ  자체의 ν•΄μ‹œ(SHA-256 λ“±)λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ•ˆμ „ν•˜κ³  μΌκ΄€λœ λ°©μ‹μž„μ„ λ°°μ› μŠ΅λ‹ˆλ‹€.
**Prevention:** μžλ™ μƒμ„±λ˜λŠ” 정적 HTML의 μ½˜ν…μΈ  λ³΄μ•ˆ μ •μ±…(CSP)μ—λŠ” `style-src 'sha256-<HASH>'` 방식을 μ μš©ν•˜κ³ , `<style>` νƒœκ·Έμ—μ„œ λΆˆν•„μš”ν•œ `nonce` 속성을 μ œκ±°ν•˜μ—¬ λΈŒλΌμš°μ €μ˜ 무결성 검증 κΈ°λŠ₯을 적극 ν™œμš©ν•˜μ‹­μ‹œμ˜€.
## 2024-07-14 - Information Exposure via Sensitive Extensions
**Vulnerability:** The `process_ignore_file` function only excluded files starting with a dot, allowing sensitive files with common extensions (e.g. `.pem`, `.key`, `.p12`, `.bak`) to be indexed and exposed to users via the generated HTML directory tree.
**Learning:** Hardcoding sensitive exclusions without dynamically evaluating standard security patterns leaves gaps in protection, specifically for files like backups or authentication keys.
**Prevention:** Implement defense-in-depth dynamic filters for known sensitive file extensions when generating directory listings.
13 changes: 11 additions & 2 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,18 @@ fun process_ignore_file(curr_dir: File, dirFilesNames: Array<String>? = null): S
val defaultSensitiveFiles = listOf(".git", ".env", ".ssh", ".htpasswd", ".htaccess", "id_rsa", "id_ed25519", "secrets.yml", ".html4ignore", ".DS_Store", ".aws", ".kube", ".npmrc", ".gnupg", "config.json", "credentials.json")
files_to_exclude.addAll(defaultSensitiveFiles)

// λ³΄μ•ˆ ν–₯상: .env, .git λ“± λ―Όκ°ν•œ 정보가 포함될 수 μžˆλŠ” μˆ¨κΉ€ 파일(.으둜 μ‹œμž‘ν•˜λŠ” λͺ¨λ“  ν•­λͺ©)을 기본적으둜 λ…ΈμΆœν•˜μ§€ μ•Šλ„λ‘ μ œμ™Έ (정보 λ…ΈμΆœ λ°©μ§€)
// λ³΄μ•ˆ ν–₯상: .env, .git λ“± μˆ¨κΉ€ 파일과 인증 ν‚€, λ°±μ—… 파일 λ“± λ―Όκ°ν•œ 정보가 포함될 수 μžˆλŠ” νŒŒμΌμ„ λ™μ μœΌλ‘œ μ œμ™Έν•˜μ—¬ 정보 λ…ΈμΆœ(Information Exposure) λ°©μ§€
(dirFilesNames ?: curr_dir.list())?.forEach {
if (it.startsWith(".")) {
val lowerName = it.toLowerCase()
if (it.startsWith(".") ||
lowerName.endsWith(".pem") ||
lowerName.endsWith(".key") ||
lowerName.endsWith(".p12") ||
lowerName.endsWith(".pfx") ||
lowerName.endsWith(".keystore") ||
lowerName.endsWith(".bak") ||
lowerName.endsWith(".swp") ||
it.endsWith("~")) {
files_to_exclude.add(it)
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/test/kotlin/html4tree/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,30 @@ class MainTest {
assertFalse(excluded.contains("test.txt"))
}

@Test
fun testProcessIgnoreFileSensitiveExtensions() {
File(tempDir, "secret.pem").createNewFile()
File(tempDir, "key.KEY").createNewFile() // test case-insensitivity
File(tempDir, "cert.p12").createNewFile()
File(tempDir, "cert.pfx").createNewFile()
File(tempDir, "app.keystore").createNewFile()
File(tempDir, "backup.bak").createNewFile()
File(tempDir, "vim.swp").createNewFile()
File(tempDir, "emacs~").createNewFile()
File(tempDir, "normal.txt").createNewFile()

val excluded = process_ignore_file(tempDir)
assertTrue(excluded.contains("secret.pem"))
assertTrue(excluded.contains("key.KEY"))
assertTrue(excluded.contains("cert.p12"))
assertTrue(excluded.contains("cert.pfx"))
assertTrue(excluded.contains("app.keystore"))
assertTrue(excluded.contains("backup.bak"))
assertTrue(excluded.contains("vim.swp"))
assertTrue(excluded.contains("emacs~"))
assertFalse(excluded.contains("normal.txt"))
}

@Test
fun testIgnoreFileIsDirectory() {
val ignoreDir = File(tempDir, ".html4ignore")
Expand Down
Loading