From b4b5a668de5383c2064d17ce70e358c9e5640f9e Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 23 Sep 2026 21:12:02 +0000 Subject: [PATCH 01/12] Fix BiDi spoofing and improve test coverage --- .jules/sentinel.md | 5 +++++ CHANGELOG.md | 2 ++ src/main/kotlin/html4tree/main.kt | 11 +++++++---- src/test/kotlin/html4tree/MainTest.kt | 12 ++++++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index a885865d..db5a8704 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -99,3 +99,8 @@ **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`). + +## 2024-05-31 - [HIGH] Prevent BiDi Spoofing and Information Concealment +**Vulnerability:** Bidirectional (BiDi) control characters can spoof filenames or extensions (e.g. `\u202E`). +**Learning:** Exposing them visually prevents malicious spoofing. Wrap user-input variables with `⁨` and `⁩` in HTML contexts. +**Prevention:** Escape BiDi chars deterministically, wrap uncontrolled HTML with FSI and PDI entities. diff --git a/CHANGELOG.md b/CHANGELOG.md index c442b3b1..1a8fc889 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ All notable changes to this project are documented in this file. ### Fixed +- 양방향(BiDi) 제어 문자를 통한 파일 이름 및 확장자 스푸핑 방지를 위해, 해당 문자를 이스케이프 처리하고 생성되는 HTML에 FSI(`⁨`) 및 PDI(`⁩`) 태그를 적용하여 입력값을 안전하게 격리하도록 수정했습니다. + - Generate the inline-style Content Security Policy SHA-256 source expression from the exact normalized UTF-8 stylesheet bytes emitted into each generated `index.html` file, preventing template whitespace from invalidating the policy. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 0972fa2c..6874e2a3 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -243,6 +243,9 @@ fun String.escapeHtml(): String { '"' -> """ '\'' -> "'" '`' -> "`" + '\u202E' -> "\\u202E" + '\u2068' -> "\\u2068" + '\u2069' -> "\\u2069" else -> null } if (replacement != null) { @@ -421,12 +424,12 @@ fun process_dir(curr_dir: File, excludeSet: Set? = null, dirFiles: Array - ${directoryName.escapeHtml()} - 디렉토리 목록 + ⁨${directoryName.escapeHtml()}⁩ - 디렉토리 목록
-

${directoryName.escapeHtml()}

+

⁨${directoryName.escapeHtml()}⁩