Skip to content
Closed
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
11 changes: 8 additions & 3 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,18 @@
**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
## 2024-05-25 - κΈ°λ³Έ 포함 및 리퍼러(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-07-07 - [Sensitive Data Exposure in Directory Indexing]
## 2024-07-07 - [디렉토리 색인 μ‹œ λ―Όκ°ν•œ 데이터 λ…ΈμΆœ]
**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-05-18 - Prevent Sensitive Information Disclosure
## 2024-05-18 - λ―Όκ°ν•œ 정보 λ…ΈμΆœ λ°©μ§€
**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.
Expand All @@ -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-14 - [html4tree] TOCTOU 파일 ꡐ체 취약점 μ™„ν™” (Atomic Move)
**Vulnerability:** `index.html` νŒŒμΌμ„ 생성할 λ•Œ μž„μ‹œ 파일(`tempPath`)을 μƒμ„±ν•˜κ³  λŒ€μƒμ„ κ΅μ²΄ν•˜λŠ” κ³Όμ •μ—μ„œ `Files.move(..., REPLACE_EXISTING)`을 μ‚¬μš©ν•˜λ©΄, ꡐ체 직전 λŒ€μƒ κ²½λ‘œκ°€ 심볼릭 λ§ν¬λ‚˜ λ‹€λ₯Έ 파일둜 λ³€κ²½λ˜λŠ” TOCTOU 취약점이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
**Learning:** 파일 ꡐ체 μž‘μ—…μ€ μ΅œλŒ€ν•œ μ›μžμ μœΌλ‘œ(atomic) μˆ˜ν–‰λ˜μ–΄μ•Ό 쀑간 κ³Όμ •μ—μ„œμ˜ 파일 μ‹œμŠ€ν…œ μ‘°μž‘μ„ λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
**Prevention:** `Files.move` 호좜 μ‹œ `StandardCopyOption.ATOMIC_MOVE` μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ—¬ μ›μžμ„±μ„ 보μž₯ν•˜λ„λ‘ μ‹œλ„ν•˜κ³ , 파일 μ‹œμŠ€ν…œμ—μ„œ μ§€μ›ν•˜μ§€ μ•Šμ„ κ²½μš°μ— λŒ€λΉ„ν•˜μ—¬ `AtomicMoveNotSupportedException`을 μž‘μ•„(catch) `REPLACE_EXISTING`으둜 μš°μ•„ν•˜κ²Œ 폴백(fallback)ν•˜λ„λ‘ κ΅¬ν˜„ν•΄μ•Ό ν•©λ‹ˆλ‹€. ν…ŒμŠ€νŠΈ 컀버리지λ₯Ό μœ„ν•΄μ„œλŠ” I/O μž‘μ—…μ„ ν•¨μˆ˜ν˜• λ§€κ°œλ³€μˆ˜λ‘œ λΆ„λ¦¬ν•˜μ—¬ μ˜ˆμ™Έλ₯Ό μ£Όμž…ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
15 changes: 13 additions & 2 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.security.MessageDigest
import java.nio.file.Files
import java.nio.file.LinkOption
import java.nio.file.StandardCopyOption
import java.nio.file.AtomicMoveNotSupportedException
import java.nio.file.attribute.BasicFileAttributes
import java.util.Base64
import com.github.ajalt.clikt.core.CliktCommand
Expand Down Expand Up @@ -229,12 +230,22 @@ 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,
moveOp: (java.nio.file.Path, java.nio.file.Path) -> Unit = { src, dest ->
Files.move(src, dest, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
}
) {
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 {
moveOp(tempPath, indexPath)
} catch (e: AtomicMoveNotSupportedException) {
Files.move(tempPath, indexPath, StandardCopyOption.REPLACE_EXISTING)
}
Comment on lines +233 to +248

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ—„οΈ Data Integrity & Integration | 🟠 Major | πŸ—οΈ Heavy lift

index.html ꡐ체 계약을 파일 μ‹œμŠ€ν…œλ³„λ‘œ μ •μ˜ν•˜κ³  κ²€μ¦ν•˜μ„Έμš”.

ATOMIC_MOVE의 κΈ°μ‘΄ λŒ€μƒ κ΅μ²΄λŠ” κ΅¬ν˜„ 쒅속일 수 μžˆμŠ΅λ‹ˆλ‹€. λΉ„μ›μž 폴백은 λ™μΌν•œ λ³΄μ•ˆμ„ 보μž₯ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ν˜„μž¬ ν…ŒμŠ€νŠΈλ„ κΈ°μ‘΄ λŒ€μƒμ΄ μ—†λŠ” 경우만 ν™•μΈν•©λ‹ˆλ‹€.

  • src/main/kotlin/html4tree/main.kt#L233-L248: κΈ°μ‘΄ λŒ€μƒ 거뢀와 μ‹€μ œ I/O 였λ₯˜λ₯Ό κ΅¬λΆ„ν•˜κ³ , ꡐ체 μ‹€νŒ¨ μ‹œ 정책을 λͺ…ν™•νžˆ ν•˜μ„Έμš”.
  • .jules/sentinel.md#L86-L90: REPLACE_EXISTING 폴백에 μž”μ—¬ TOCTOU μœ„ν—˜μ΄ μžˆμŒμ„ κΈ°λ‘ν•˜μ„Έμš”.
  • src/test/kotlin/html4tree/MainTest.kt#L349-L359: κΈ°μ‘΄ index.html을 λ§Œλ“€κ³  폴백 ν›„ μƒˆ λ‚΄μš©μœΌλ‘œ κ΅μ²΄λ˜λŠ”μ§€ κ²€μ¦ν•˜μ„Έμš”.
🧰 Tools
πŸͺ› detekt (1.23.8)

[warning] 246-246: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

πŸ“ Affects 3 files
  • src/main/kotlin/html4tree/main.kt#L233-L248 (this comment)
  • .jules/sentinel.md#L86-L90
  • src/test/kotlin/html4tree/MainTest.kt#L349-L359
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/html4tree/main.kt` around lines 233 - 248, Update
write_index_file in src/main/kotlin/html4tree/main.kt at lines 233-248 to
distinguish an existing-target rejection from other I/O failures and explicitly
define the replacement-failure policy; update .jules/sentinel.md at lines 86-90
to document the remaining TOCTOU risk of the REPLACE_EXISTING fallback; update
src/test/kotlin/html4tree/MainTest.kt at lines 349-359 to create an existing
index.html and verify that fallback replacement writes the new content.

} finally {
Files.deleteIfExists(tempPath)
}
Expand Down
11 changes: 11 additions & 0 deletions src/test/kotlin/html4tree/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,17 @@ class MainTest {
assertTrue(htmlContent.contains("margin: 0 auto;"))
}

@Test
fun testWriteIndexFileAtomicMoveFallback() {
val indexFile = File(tempDir, "index.html")
val content = "atomic fallback content"
write_index_file(tempDir, content, moveOp = { src, dest ->
throw java.nio.file.AtomicMoveNotSupportedException(src.toString(), dest.toString(), "Mocked exception")
})
assertTrue(indexFile.exists(), "Index file should exist after fallback move")
assertEquals(content, indexFile.readText())
}

@Test
fun testWriteIndexFileCleansUpTempFileOnFailure() {
// Files.move cannot replace a non-empty directory, so this drives the
Expand Down
Loading