From 0b6dc30695f76de45fe34d2b52072962a48bb5c2 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 3 Jul 2026 03:21:17 +0000 Subject: [PATCH] Fix path traversal via symbolic link canonicalization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit canonicalFile을 absoluteFile로 변경하여 심볼릭 링크를 이용한 검사 우회를 방지합니다. --- .jules/sentinel.md | 5 +++++ src/main/kotlin/html4tree/main.kt | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 6c61284..ab1395d 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -22,3 +22,8 @@ **Vulnerability:** Defense in Depth (CSP Missing) **Learning:** Even when inputs are properly escaped, statically generated HTML that displays file/directory structures should implement a Content Security Policy (CSP) to provide an extra layer of defense against potential XSS bypasses. **Prevention:** Include a strict CSP meta tag (e.g., `default-src 'none'; style-src 'unsafe-inline';`) in auto-generated HTML headers when external scripts or resources are not required. + +## 2024-07-03 - [Path Traversal via Symbolic Link Canonicalization] +**Vulnerability:** `File(path).canonicalFile`을 사용하여 경로를 정규화한 후 해당 파일 객체에 대해 심볼릭 링크 여부를 검사하면, 이미 심볼릭 링크가 원본 경로로 해석된 상태이므로 `Files.isDirectory(..., LinkOption.NOFOLLOW_LINKS)` 같은 심볼릭 링크 검사가 실패(우회)합니다. +**Learning:** `canonicalFile`은 모든 심볼릭 링크를 해제하고 절대 경로를 반환하므로, 심볼릭 링크 자체의 특성을 확인해야 할 때는 사용해서는 안 됩니다. 이로 인해 애플리케이션의 제어 범위를 벗어난 디렉토리로 Path Traversal이 가능해집니다. +**Prevention:** 심볼릭 링크를 검사하거나 파일 시스템 경계를 유지해야 할 때는 `canonicalFile` 대신 `absoluteFile`을 사용하여 입력된 경로의 원래 형태(심볼릭 링크 포함)를 보존한 채로 검사를 수행해야 합니다. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 2e2809f..1af7951 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -23,7 +23,9 @@ fun main(args: Array) = Html4tree().main(args) fun go(topDir: String, maxLevel: Int) { require(topDir.isNotBlank()) - val top_dir = File(topDir).canonicalFile + // 🛡️ Sentinel: Path Traversal 및 심볼릭 링크를 이용한 검사 우회 방지를 위해 + // canonicalFile 대신 absoluteFile을 사용하여 원본 경로 형태를 유지합니다. + val top_dir = File(topDir).absoluteFile require(Files.isDirectory(top_dir.toPath(), LinkOption.NOFOLLOW_LINKS)) { "Top directory must be an existing non-symlink directory" } val ll = LinkedList()