From c50e3f4ad5f6997d7502d615b59115b95e46aaf7 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 18 Jul 2026 23:46:49 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20[UX=20improvement]=20?= =?UTF-8?q?=EB=B0=B0=EC=B9=98=20=ED=8C=8C=EC=9D=BC=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=ED=81=AC=EA=B8=B0=20=EA=B2=80=EC=A6=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/palette.md | 4 ++++ CHANGELOG.md | 1 + pr_description.md | 12 ++++-------- saas_web.py | 8 ++++++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.jules/palette.md b/.jules/palette.md index a1cf208b..8054944d 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -1,3 +1,7 @@ +## 2024-07-14 - 파일 업로드 크기 제한 클라이언트 검증 추가 +**Learning:** 사용자가 백엔드의 최대 파일 업로드 제한(예: 5 GiB)을 초과하는 대용량 파일을 일괄 업로드 폼에서 선택했을 때, 클라이언트 측에서 즉각적인 경고가 없다면 사용자는 긴 업로드 시간을 소모한 뒤에야 서버로부터 에러를 반환받게 되어 매우 나쁜 사용자 경험(UX)을 겪습니다. 특히 여러 파일을 선택하는 경우 크기 합산을 직관적으로 인지하기 어렵습니다. +**Action:** 파일 업로드 폼(특히 multiple 속성이 있는 일괄 업로드 폼)에서는 항상 JavaScript의 `onchange` 이벤트를 통해 선택된 파일들의 총 크기를 합산하고, 백엔드의 제한 크기(예: `MAX_UPLOAD_BYTES`)와 비교해야 합니다. 제한을 초과할 경우 `setCustomValidity`를 통한 커스텀 에러 메시지 설정, `aria-invalid="true"`를 통한 접근성 피드백, 그리고 명확한 텍스트 및 색상 변화를 활용한 인라인 에러 미리보기를 제공하여 제출 전 즉각적인 피드백을 제공하십시오. + ## 2024-07-12 - Intercepting batch form submissions for testing visual loading states **Learning:** Extending the learning from 2024-06-13, intercepting form submissions using `e.preventDefault()` via `page.evaluate()` is essential for capturing screenshot and video evidence of loading states (e.g., button disabling, spinner appearing) on forms like batch upload where the submission would normally reload the page or download an archive. **Action:** When testing visual loading states with Playwright, always inject an event listener using `page.evaluate()` to call `e.preventDefault()` on the form's `submit` event to freeze the UI in its loading state for verification. diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfe94a6..78da83ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## [Unreleased] +- 🎨 Palette: [UX improvement] 배치 파일 업로드 시 총 파일 크기가 5 GiB를 초과하는 경우 클라이언트 측 검증(client-side validation) 및 UI 피드백 추가 ### Added - 다중 파일 업로드 선택 시 즉각적인 파일 개수 피드백 및 제한 초과 경고 메시지 추가 - 일괄 업로드 폼에 대상 바이트 프리셋 버튼과 총 파일 크기 미리보기를 추가하여 사용성을 개선했습니다. diff --git a/pr_description.md b/pr_description.md index 28561186..4b7e69d0 100644 --- a/pr_description.md +++ b/pr_description.md @@ -1,8 +1,4 @@ -🚨 **Severity:** CRITICAL -💡 **Vulnerability:** `media_shrinker.py`의 `convert_file` 함수가 `Path.relative_to()`를 해결되지 않은 경로에 직접 적용해, `..` 세그먼트 또는 symlink escape가 루트 경계 검증을 우회할 수 있었습니다. -🎯 **Impact:** 루트 밖 입력 파일이 변환 대상으로 들어오면 의도하지 않은 파일 읽기 및 출력 경로 계획으로 이어질 수 있습니다. -🔧 **Fix:** source/root를 한 번만 `resolve()`한 뒤 resolved source가 resolved root 아래인지 검사하고, 실패 시 내부 경로를 노출하지 않는 `MediaShrinkerError`로 중단합니다. 기존 Sentinel 보안 학습 이력은 유지하고 새 path traversal 항목만 추가했습니다. -✅ **Verification:** -1. `python3 -m pytest tests/test_security_path_traversal.py -q` → 2 passed. -2. `python3 -m pytest -q` → 145 passed. -3. `python3 -m py_compile media_shrinker.py saas_web.py mcp_driver.py job_store.py && python3 -m unittest discover -s tests -v` → 145 tests OK. +💡 What: 배치 파일 업로드 시 총 용량이 백엔드 제한(5 GiB)을 초과하는지 클라이언트에서 미리 검증하도록 변경 +🎯 Why: 사용자가 긴 업로드 시간을 기다린 후 서버에서 거부되는 문제를 방지하여 UX 개선 +📸 Before/After: 크기 초과 시 즉각적인 인라인 에러 메시지 표시 +♿ Accessibility: aria-invalid='true' 및 setCustomValidity를 통한 접근성 개선 피드백 제공 diff --git a/saas_web.py b/saas_web.py index 3a7b0352..01b8c473 100644 --- a/saas_web.py +++ b/saas_web.py @@ -318,6 +318,14 @@ async def add_security_headers(request: Request, call_next): totalSize += files[i].size; } + if (totalSize > MAX_UPLOAD_BYTES) { + input.setCustomValidity('Total file size exceeds 5 GiB limit.'); + input.setAttribute('aria-invalid', 'true'); + preview.innerText = 'Selected ' + files.length + ' files (' + formatBinaryBytes(totalSize) + ', exceeds 5 GiB limit)'; + preview.style.color = '#dc3545'; + return; + } + if (files.length > 20) { input.setCustomValidity('Maximum is 20 files per batch.'); input.setAttribute('aria-invalid', 'true');