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
4 changes: 4 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## [Unreleased]
- ๐ŸŽจ Palette: [UX improvement] ๋ฐฐ์น˜ ํŒŒ์ผ ์—…๋กœ๋“œ ์‹œ ์ด ํŒŒ์ผ ํฌ๊ธฐ๊ฐ€ 5 GiB๋ฅผ ์ดˆ๊ณผํ•˜๋Š” ๊ฒฝ์šฐ ํด๋ผ์ด์–ธํŠธ ์ธก ๊ฒ€์ฆ(client-side validation) ๋ฐ UI ํ”ผ๋“œ๋ฐฑ ์ถ”๊ฐ€
### Added
- ๋‹ค์ค‘ ํŒŒ์ผ ์—…๋กœ๋“œ ์„ ํƒ ์‹œ ์ฆ‰๊ฐ์ ์ธ ํŒŒ์ผ ๊ฐœ์ˆ˜ ํ”ผ๋“œ๋ฐฑ ๋ฐ ์ œํ•œ ์ดˆ๊ณผ ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€ ์ถ”๊ฐ€
- ์ผ๊ด„ ์—…๋กœ๋“œ ํผ์— ๋Œ€์ƒ ๋ฐ”์ดํŠธ ํ”„๋ฆฌ์…‹ ๋ฒ„ํŠผ๊ณผ ์ด ํŒŒ์ผ ํฌ๊ธฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์‚ฌ์šฉ์„ฑ์„ ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.
12 changes: 4 additions & 8 deletions pr_description.md
Original file line number Diff line number Diff line change
@@ -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๋ฅผ ํ†ตํ•œ ์ ‘๊ทผ์„ฑ ๊ฐœ์„  ํ”ผ๋“œ๋ฐฑ ์ œ๊ณต
8 changes: 8 additions & 0 deletions saas_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Loading