From 62fc79b9391f918f9feeafcd39a68039dcbedf8e Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 4 Sep 2026 21:06:59 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]=20Fi?= =?UTF-8?q?x=20TOCTOU=20vulnerability=20in=20file=20reading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * `File.useLines` internally resolves symbolic links. We need to prevent following symlinks when reading `.html4ignore`. * Replaced `ignore_file.useLines` with `Files.newInputStream(ignore_file.toPath(), StandardOpenOption.READ, LinkOption.NOFOLLOW_LINKS).bufferedReader().useLines`. * Appended findings to `.jules/sentinel.md` journal. --- .jules/sentinel.md | 4 ++++ src/main/kotlin/html4tree/main.kt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index a885865d..16b6b85a 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -99,3 +99,7 @@ **Root cause:** The protected implementation added canonical names to the exclusion set but did not compare each observed directory entry through a locale-stable normalized key. **Prevention:** Build one `Locale.ROOT` lowercase set from the canonical sensitive names, compare every observed name against it, and add the original spelling to the exclusion set so downstream exact membership remains correct. **Evidence:** `testProcessIgnoreFileTreatsSensitiveNamesCaseInsensitively` failed on test-only commit `472b916cd40f70693c4e1eb48956042a25353feb` (CI run `31469596932`) and passed with the source fix at `bb113d858ccfc42ddaecf6729749b238e5ade2d0` (CI run `31469921661`). +## 2026-09-04 - Prevent TOCTOU in file reading +**Vulnerability:** `File.useLines` implicitly resolves symbolic links internally, which can lead to Time-of-Check-to-Time-of-Use (TOCTOU) if the file is replaced by a symlink after validation. +**Learning:** Even if symlinks are verified as absent prior to reading, Kotlin's standard library file readers might still traverse them during the reading phase, opening a TOCTOU window. +**Prevention:** Replace `File.useLines` with `Files.newInputStream(..., StandardOpenOption.READ, LinkOption.NOFOLLOW_LINKS).bufferedReader().useLines` to strictly disallow symlink traversal when opening the file. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 0972fa2c..e914b54a 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -309,7 +309,7 @@ fun process_ignore_file(curr_dir: File, dirFilesNames: Array? = null): S if(ignore_file.isFile && !Files.isSymbolicLink(ignore_file.toPath()) && ignore_file.canRead() && ignore_file.length() <= 1048576){ val ignored_matchers = mutableListOf() - ignore_file.useLines { lines -> + java.nio.file.Files.newInputStream(ignore_file.toPath(), java.nio.file.StandardOpenOption.READ, java.nio.file.LinkOption.NOFOLLOW_LINKS).bufferedReader().useLines { lines -> for ((lineIndex, it) in lines.withIndex()) { // 줄 수 제한이 패턴 수도 함께 상한(줄당 최대 1개 패턴)하므로 별도 패턴 카운터는 불필요 if (lineIndex >= 1000) break