Skip to content
Open
29 changes: 17 additions & 12 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,28 @@
**Learning:** ํ์— ๋„ฃ๊ธฐ ์ „(`listFiles`)์— ํ•œ ๋ฒˆ ๊ฒ€์‚ฌํ–ˆ๋‹ค๊ณ  ํ•ด์„œ, ํ์—์„œ ๋นผ๋‚ด์–ด ์ฒ˜๋ฆฌ(`process_dir`)ํ•˜๋Š” ์‹œ์ ์—๋„ ํŒŒ์ผ ์‹œ์Šคํ…œ ์ƒํƒœ๊ฐ€ ๋™์ผํ•  ๊ฒƒ์ด๋ผ๊ณ  ๊ฐ€์ •(Implicit Trust)ํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.
**Prevention:** ํ์— ๋„ฃ๋Š” ์‹œ์ (`Time-of-Check`)์— ํŒŒ์ผ์˜ ๊ณ ์œ  ์‹๋ณ„์ž(`BasicFileAttributes.fileKey()`)๋ฅผ ์บก์ฒ˜ํ•ด๋‘๊ณ , ํ์—์„œ ๊บผ๋‚ด์–ด ์‹ค์ œ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์‹œ์ (`Time-of-Use`)์— ํ˜„์žฌ ํŒŒ์ผ์˜ `fileKey()`๋ฅผ ๋‹ค์‹œ ์ฝ์–ด ๋‘ ๊ฐ’์ด ์ผ์น˜ํ•˜๋Š”์ง€ ์žฌ๊ฒ€์ฆ(Re-verify)ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

## 2024-05-25 - Information Exposure via Default Inclusion and Referrer
**Vulnerability:** Common sensitive files (like `.aws`, `.kube`, `.npmrc`) could be accidentally indexed if present in the tree. Furthermore, clicking on external links (if any were added) could leak the directory structure via the HTTP Referer header.
**Learning:** Default exclude lists must encompass modern toolchains and cloud credentials, as users often run directory indexers in their home or project root directories. HTML templates need explicit policies to prevent accidental data leakage via headers.
**Prevention:** Maintain an extensive default deny-list for known sensitive files and enforce `no-referrer` globally on generated index pages.
## 2024-05-25 - ๊ธฐ๋ณธ ํฌํ•จ ๋ฐ Referrer๋ฅผ ํ†ตํ•œ ์ •๋ณด ๋…ธ์ถœ (Information Exposure)
**Vulnerability:** `.aws`, `.kube`, `.npmrc` ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ํŒŒ์ผ๋“ค์ด ํŠธ๋ฆฌ์— ์กด์žฌํ•  ๊ฒฝ์šฐ ์‹ค์ˆ˜๋กœ ์ธ๋ฑ์‹ฑ๋  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋”๋ถˆ์–ด ์ถ”๊ฐ€๋œ ์™ธ๋ถ€ ๋งํฌ๋ฅผ ํด๋ฆญํ•  ๋•Œ HTTP Referer ํ—ค๋”๋ฅผ ํ†ตํ•ด ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๊ฐ€ ์œ ์ถœ๋  ์œ„ํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
**Learning:** ์‚ฌ์šฉ์ž๋“ค์ด ์ข…์ข… ํ™ˆ ๋””๋ ‰ํ† ๋ฆฌ๋‚˜ ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์—์„œ ๋””๋ ‰ํ† ๋ฆฌ ์ธ๋ฑ์„œ๋ฅผ ์‹คํ–‰ํ•˜๋ฏ€๋กœ, ์ตœ์‹  ๋„๊ตฌ ์ฒด์ธ ๋ฐ ํด๋ผ์šฐ๋“œ ์ž๊ฒฉ ์ฆ๋ช…์„ ํฌ๊ด„ํ•˜๋Š” ๊ธฐ๋ณธ ์ œ์™ธ ๋ชฉ๋ก์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค. HTML ํ…œํ”Œ๋ฆฟ์—๋Š” ํ—ค๋”๋ฅผ ํ†ตํ•œ ์šฐ๋ฐœ์  ๋ฐ์ดํ„ฐ ์œ ์ถœ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋ช…์‹œ์  ์ •์ฑ…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
**Prevention:** ๋„๋ฆฌ ์•Œ๋ ค์ง„ ๋ฏผ๊ฐํ•œ ํŒŒ์ผ๋“ค์— ๋Œ€ํ•œ ๊ด‘๋ฒ”์œ„ํ•œ ๊ธฐ๋ณธ ๊ฑฐ๋ถ€(deny-list) ๋ชฉ๋ก์„ ์œ ์ง€ํ•˜๊ณ , ์ƒ์„ฑ๋œ ์ธ๋ฑ์Šค ํŽ˜์ด์ง€์—์„œ ์ „์—ญ์ ์œผ๋กœ `no-referrer`๋ฅผ ๊ฐ•์ œํ•˜์‹ญ์‹œ์˜ค.

## 2024-07-07 - [Sensitive Data Exposure in Directory Indexing]
**Vulnerability:** The application was traversing and listing hidden files and directories (those starting with `.`), potentially exposing sensitive information like `.git` histories or `.env` configuration files in the generated HTML index.
**Learning:** This existed because the traversal and filtering logic did not explicitly account for standard conventions regarding hidden files, defaulting to listing everything not explicitly ignored.
**Prevention:** Always implement explicit filters for hidden files and directories (e.g., `!file.name.startsWith(".")`) in applications that generate static files or expose directory structures to the public.
## 2024-07-07 - [๋””๋ ‰ํ† ๋ฆฌ ์ธ๋ฑ์‹ฑ ์ค‘ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ ๋…ธ์ถœ]
**Vulnerability:** ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ˆจ๊น€ ํŒŒ์ผ ๋ฐ ๋””๋ ‰ํ† ๋ฆฌ(`. `๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ•ญ๋ชฉ)๋ฅผ ์ˆœํšŒํ•˜๊ณ  ๋‚˜์—ดํ•˜์—ฌ, ์ƒ์„ฑ๋œ HTML ์ธ๋ฑ์Šค์—์„œ `.git` ํžˆ์Šคํ† ๋ฆฌ๋‚˜ `.env` ๊ตฌ์„ฑ ํŒŒ์ผ ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ๋…ธ์ถœํ•  ์ž ์žฌ์  ์œ„ํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
**Learning:** ๋ช…์‹œ์ ์œผ๋กœ ๋ฌด์‹œ๋˜์ง€ ์•Š์€ ๋ชจ๋“  ๊ฒƒ์„ ๋‚˜์—ดํ•˜๋Š” ๊ธฐ๋ณธ ๋™์ž‘ ๋•Œ๋ฌธ์—, ์ˆœํšŒ ๋ฐ ํ•„ํ„ฐ๋ง ๋กœ์ง์ด ์ˆจ๊น€ ํŒŒ์ผ์— ๋Œ€ํ•œ ์ผ๋ฐ˜์ ์ธ ๊ทœ์น™์„ ์ œ๋Œ€๋กœ ๊ณ ๋ คํ•˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.
**Prevention:** ์ •์  ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋Œ€์ค‘์—๊ฒŒ ๋…ธ์ถœํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ํ•ญ์ƒ ์ˆจ๊น€ ํŒŒ์ผ ๋ฐ ๋””๋ ‰ํ† ๋ฆฌ์— ๋Œ€ํ•œ ๋ช…์‹œ์ ์ธ ํ•„ํ„ฐ(์˜ˆ: `!file.name.startsWith(".")`)๋ฅผ ๊ตฌํ˜„ํ•˜์‹ญ์‹œ์˜ค.


## 2024-05-18 - Prevent Sensitive Information Disclosure
**Vulnerability:** The application lists all files in a directory, including hidden files (those starting with `.`), which could inadvertently expose sensitive information like `.env`, `.git`, or `.ssh` directories.
**Learning:** Default directory listing implementations without hidden file filtering can lead to information disclosure vulnerabilities when serving directories containing configuration or sensitive files.
**Prevention:** Automatically exclude hidden files (files starting with `.`) from the generated directory listing by default.
## 2024-05-18 - ๋ฏผ๊ฐํ•œ ์ •๋ณด ๋…ธ์ถœ ๋ฐฉ์ง€
**Vulnerability:** ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด์˜ ๋ชจ๋“  ํŒŒ์ผ(์ˆจ๊น€ ํŒŒ์ผ ํฌํ•จ)์„ ๋‚˜์—ดํ•˜์—ฌ `.env`, `.git` ๋˜๋Š” `.ssh` ๋””๋ ‰ํ† ๋ฆฌ์™€ ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์‹ค์ˆ˜๋กœ ๋…ธ์ถœํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
**Learning:** ์ˆจ๊น€ ํŒŒ์ผ ํ•„ํ„ฐ๋ง์ด ์—†๋Š” ๊ธฐ๋ณธ ๋””๋ ‰ํ† ๋ฆฌ ๋‚˜์—ด ๊ตฌํ˜„์€, ๊ตฌ์„ฑ ํŒŒ์ผ์ด๋‚˜ ๋ฏผ๊ฐํ•œ ํŒŒ์ผ์ด ํฌํ•จ๋œ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์„œ๋น„์Šคํ•  ๋•Œ ์ •๋ณด ๋…ธ์ถœ ์ทจ์•ฝ์ ์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
**Prevention:** ์ƒ์„ฑ๋œ ๋””๋ ‰ํ† ๋ฆฌ ๋ชฉ๋ก์—์„œ ์ˆจ๊น€ ํŒŒ์ผ(`. `๋กœ ์‹œ์ž‘ํ•˜๋Š” ํŒŒ์ผ)์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž๋™ ์ œ์™ธํ•˜์‹ญ์‹œ์˜ค.

## 2024-07-13 - [MEDIUM] ์ •์  ๋ฆฌ์†Œ์Šค์šฉ CSP ์ƒ์„ฑ ๋ฐฉ์‹(Nonce ๋Œ€์‹  ํ•ด์‹œ) ๊ด€๋ จ ๋ณด์•ˆ ๊ฐ•ํ™”
**Vulnerability:** ์ •์  HTML ์ƒ์„ฑ ๋„๊ตฌ์—์„œ ๋งค๋ฒˆ ๋‹ค๋ฅธ Nonce๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ CSP์— ์ ์šฉํ•˜๋Š” ๊ฒƒ์€, ์บ์‹ฑ ํšจ์œจ์„ ์ €ํ•˜์‹œํ‚ฌ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ •์  ๋ฐฐํฌ ํ™˜๊ฒฝ(์˜ˆ: GitHub Pages ๋“ฑ)์—์„œ ์˜ฌ๋ฐ”๋ฅธ ๋ณด์•ˆ ์ •์ฑ… ์ˆ˜๋ฆฝ์„ ๋ฐฉํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์•ˆํ‹ฐ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
**Learning:** ์ •์ ์œผ๋กœ ๊ณ ์ •๋œ ์ธ๋ผ์ธ ์Šคํƒ€์ผ์ด๋‚˜ ์Šคํฌ๋ฆฝํŠธ์—๋Š” ๋‚œ์ˆ˜ํ™”๋œ Nonce๋ณด๋‹ค ์ฝ˜ํ…์ธ  ์ž์ฒด์˜ ํ•ด์‹œ(SHA-256 ๋“ฑ)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜๊ณ  ์ผ๊ด€๋œ ๋ฐฉ์‹์ž„์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค.
**Prevention:** ์ž๋™ ์ƒ์„ฑ๋˜๋Š” ์ •์  HTML์˜ ์ฝ˜ํ…์ธ  ๋ณด์•ˆ ์ •์ฑ…(CSP)์—๋Š” `style-src 'sha256-<HASH>'` ๋ฐฉ์‹์„ ์ ์šฉํ•˜๊ณ , `<style>` ํƒœ๊ทธ์—์„œ ๋ถˆํ•„์š”ํ•œ `nonce` ์†์„ฑ์„ ์ œ๊ฑฐํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์˜ ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ ๊ธฐ๋Šฅ์„ ์ ๊ทน ํ™œ์šฉํ•˜์‹ญ์‹œ์˜ค.

## 2024-07-14 - [MEDIUM] ์›์ž์  ํŒŒ์ผ ๊ต์ฒด(Atomic Move) ํด๋ฐฑ ๊ตฌํ˜„
**Vulnerability:** ์ •์  HTML ์ธ๋ฑ์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ธฐ์กด ํŒŒ์ผ์„ ๊ต์ฒด(`Files.move`)ํ•˜๋Š” ๊ณผ์ •์—์„œ `ATOMIC_MOVE` ์˜ต์…˜ ๋ถ€์žฌ๋กœ ์ธํ•ด, ๋™์‹œ ์ฝ๊ธฐ๋‚˜ ์‹œ์Šคํ…œ ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ Race Condition ๋˜๋Š” ๋ถˆ์™„์ „ํ•œ ํŒŒ์ผ ์“ฐ๊ธฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
**Learning:** ํŒŒ์ผ ์‹œ์Šคํ…œ ์กฐ์ž‘ ์‹œ ๊ฐ•๋ ฅํ•œ ์›์ž์„ฑ(Atomicity)์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” `StandardCopyOption.ATOMIC_MOVE`๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜์ง€๋งŒ, ์ผ๋ถ€ ํŒŒ์ผ ์‹œ์Šคํ…œ์ด๋‚˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์ด ์˜ต์…˜์„ ์ง€์›ํ•˜์ง€ ์•Š์•„ `AtomicMoveNotSupportedException`์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
**Prevention:** `Files.move`๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ `ATOMIC_MOVE` ์˜ต์…˜์„ ์šฐ์„  ์‹œ๋„ํ•˜๋˜, `AtomicMoveNotSupportedException`์ด ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ `StandardCopyOption.REPLACE_EXISTING`์œผ๋กœ ์ •์ƒ์ ์œผ๋กœ Fallback(์šฐ์•„ํ•œ ์‹คํŒจ) ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฐฉ์–ด์ ์ธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•˜์‹ญ์‹œ์˜ค.
12 changes: 10 additions & 2 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,20 @@ fun process_ignore_file(curr_dir: File, dirFilesNames: Array<String>? = null): S
return files_to_exclude
}

fun write_index_file(curr_dir: File, content: String) {
fun write_index_file(
curr_dir: File,
content: String,
moveFile: (java.nio.file.Path, java.nio.file.Path, Array<out java.nio.file.CopyOption>) -> java.nio.file.Path = Files::move
) {
val indexPath = curr_dir.toPath().resolve("index.html")
val tempPath = Files.createTempFile(curr_dir.toPath(), ".index-", ".html")
try {
Files.write(tempPath, content.toByteArray(Charsets.UTF_8))
Files.move(tempPath, indexPath, StandardCopyOption.REPLACE_EXISTING)
try {
moveFile(tempPath, indexPath, arrayOf(StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING))
} catch (e: java.nio.file.AtomicMoveNotSupportedException) {
moveFile(tempPath, indexPath, arrayOf(StandardCopyOption.REPLACE_EXISTING))
}
} finally {
Files.deleteIfExists(tempPath)
}
Expand Down
16 changes: 16 additions & 0 deletions src/test/kotlin/html4tree/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ class MainTest {
}
}

@Test
fun testWriteIndexFileAtomicMoveFallback() {
var fallbackCalled = false
write_index_file(tempDir, "content") { source, target, options ->
if (options.contains(java.nio.file.StandardCopyOption.ATOMIC_MOVE)) {
throw java.nio.file.AtomicMoveNotSupportedException(source.toString(), target.toString(), "Mocked exception")
}
fallbackCalled = true
java.nio.file.Files.move(source, target, *options)
}
assertTrue(fallbackCalled, "Fallback to REPLACE_EXISTING should be called")
val indexFile = File(tempDir, "index.html")
assertTrue(indexFile.exists())
assertEquals("content", indexFile.readText())
}

@Test
fun testEscapeHtml() {
assertEquals("&amp;", "&".escapeHtml())
Expand Down
Loading