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 @@ -83,3 +83,8 @@
**Vulnerability:** 정적 HTML 생성 λ„κ΅¬μ—μ„œ 맀번 λ‹€λ₯Έ Nonceλ₯Ό λ™μ μœΌλ‘œ μƒμ„±ν•˜μ—¬ CSP에 μ μš©ν•˜λŠ” 것은, 캐싱 νš¨μœ¨μ„ μ €ν•˜μ‹œν‚¬ 뿐만 μ•„λ‹ˆλΌ 정적 배포 ν™˜κ²½(예: GitHub Pages λ“±)μ—μ„œ μ˜¬λ°”λ₯Έ λ³΄μ•ˆ μ •μ±… μˆ˜λ¦½μ„ λ°©ν•΄ν•  수 μžˆλŠ” μ•ˆν‹° νŒ¨ν„΄μž…λ‹ˆλ‹€.
**Learning:** μ •μ μœΌλ‘œ κ³ μ •λœ 인라인 μŠ€νƒ€μΌμ΄λ‚˜ μŠ€ν¬λ¦½νŠΈμ—λŠ” λ‚œμˆ˜ν™”λœ Nonce보닀 μ½˜ν…μΈ  자체의 ν•΄μ‹œ(SHA-256 λ“±)λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ•ˆμ „ν•˜κ³  μΌκ΄€λœ λ°©μ‹μž„μ„ λ°°μ› μŠ΅λ‹ˆλ‹€.
**Prevention:** μžλ™ μƒμ„±λ˜λŠ” 정적 HTML의 μ½˜ν…μΈ  λ³΄μ•ˆ μ •μ±…(CSP)μ—λŠ” `style-src 'sha256-<HASH>'` 방식을 μ μš©ν•˜κ³ , `<style>` νƒœκ·Έμ—μ„œ λΆˆν•„μš”ν•œ `nonce` 속성을 μ œκ±°ν•˜μ—¬ λΈŒλΌμš°μ €μ˜ 무결성 검증 κΈ°λŠ₯을 적극 ν™œμš©ν•˜μ‹­μ‹œμ˜€.

## 2024-07-16 - [MEDIUM] μ›μžμ  파일 μ“°κΈ°λ₯Ό ν†΅ν•œ λΆˆμ™„μ „ν•œ μƒνƒœ λ…ΈμΆœ μ™„ν™”
**Vulnerability:** 파일 ꡐ체 μ‹œ `ATOMIC_MOVE` μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©΄, `index.html` 파일이 μƒμ„±λ˜κ±°λ‚˜ κ΅μ²΄λ˜λŠ” 도쀑에 μ‚¬μš©μžκ°€ μ ‘κ·Όν•  경우 파일이 λΉ„μ–΄μžˆκ±°λ‚˜ λΆˆμ™„μ „ν•œ μƒνƒœλ‘œ 읽힐 수 μžˆμŠ΅λ‹ˆλ‹€ (경쟁 쑰건 λ°œμƒ).
**Learning:** 파일 ꡐ체 μž‘μ—…μ€ μ›μžμ (Atomic)이어야 ν•©λ‹ˆλ‹€. 특히 μž„μ‹œ νŒŒμΌμ—μ„œ μ΅œμ’… 파일둜 λ³€κ²½ν•  λ•Œ 파일 μ‹œμŠ€ν…œμ˜ `ATOMIC_MOVE` 볡사 μ˜΅μ…˜μ„ μ‚¬μš©ν•˜κ³ , μ§€μ›λ˜μ§€ μ•Šμ„ 경우 μ•ˆμ „ν•˜κ²Œ λŒ€μ²΄ μ˜΅μ…˜μœΌλ‘œ 폴백해야 ν•œλ‹€λŠ” 점을 λ°°μ› μŠ΅λ‹ˆλ‹€.
**Prevention:** `Files.move`λ₯Ό μ‚¬μš©ν•  λ•Œ `StandardCopyOption.ATOMIC_MOVE`λ₯Ό κΈ°λ³Έ μ‹œλ„ μ˜΅μ…˜μœΌλ‘œ μ‚¬μš©ν•˜κ³ , `AtomicMoveNotSupportedException`이 λ°œμƒν•  경우 `StandardCopyOption.REPLACE_EXISTING`으둜 μš°μ•„ν•˜κ²Œ(gracefully) μ²˜λ¦¬ν•˜λ„λ‘ μ½”λ“œλ₯Ό κ΅¬μ„±ν•˜μ‹­μ‹œμ˜€.
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<java.nio.file.CopyOption>) -> java.nio.file.Path = { src, dest, options -> Files.move(src, dest, *options) }
) {
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
23 changes: 23 additions & 0 deletions src/test/kotlin/html4tree/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,29 @@ class MainTest {
assertTrue(htmlContent.contains("margin: 0 auto;"))
}

@Test
fun testWriteIndexFileAtomicMoveFallback() {
val indexDir = File(tempDir, "fallback")
indexDir.mkdir()
var fallbackExecuted = false
write_index_file(
indexDir,
"content",
moveFile = { src, dest, options ->
if (options.contains(java.nio.file.StandardCopyOption.ATOMIC_MOVE)) {
throw java.nio.file.AtomicMoveNotSupportedException(src.toString(), dest.toString(), "Mocked exception")
} else {
fallbackExecuted = true
java.nio.file.Files.move(src, dest, *options)
}
}
)
assertTrue(fallbackExecuted, "Fallback to non-atomic move should be executed")
val indexFile = File(indexDir, "index.html")
assertTrue(indexFile.exists())
assertEquals("content", indexFile.readText())
}

@Test
fun testWriteIndexFileCleansUpTempFileOnFailure() {
// Files.move cannot replace a non-empty directory, so this drives the
Expand Down
22 changes: 22 additions & 0 deletions test_temp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption

fun write_index_file(
curr_dir: File,
content: String,
moveFile: (java.nio.file.Path, java.nio.file.Path, Array<java.nio.file.CopyOption>) -> java.nio.file.Path = { src, dest, options -> Files.move(src, dest, *options) }
) {
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))
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)
}
}
Loading