fix(tradein): санитизация brand.logo_url + валидация brand-цветов (#766) #791

Merged
bot-reviewer merged 1 commit from feat/766-brand-sanitize into main 2026-05-30 16:58:14 +00:00
Collaborator

Summary (security, чейн с #765)

  • Topbar: brand.logo_url обёрнут в safeUrl (блокирует javascript:/data:/прочие не-http(s) схемы). Невалидный URL → fallback на TI-марку (без broken/опасного <img>), brand.name сохраняется.
  • page.tsx: primary_color/accent_color валидируются regex (hex / rgb() / hsl()) перед setProperty — невалидный цвет не применяется (остаётся дефолт), защита от CSS-инъекции подменённым бренд-конфигом.
  • Happy-path (валидный бренд praktika: лого + цвета) не тронут.

Why

Бренд-поля рендерились без санитизации; в чейне с ?brand= override (#765) подменённый бренд был вектором для javascript:-URL в логотипе / CSS-инъекции в цветах.

Test plan

  • npx tsc --noEmit → 0 ошибок
  • npm run build → ✓ Compiled successfully, 10/10 pages
  • grep safeUrl Topbar.tsx → ≥1; grep 'test(' page.tsx → 2 (оба цвета под guard)
  • Smoke: praktika-бренд рендерит лого+цвета как сейчас; javascript:-logo → TI-марка (qa)

Refs #766

## Summary (security, чейн с #765) - **Topbar**: `brand.logo_url` обёрнут в `safeUrl` (блокирует `javascript:`/`data:`/прочие не-http(s) схемы). Невалидный URL → fallback на `TI`-марку (без broken/опасного `<img>`), `brand.name` сохраняется. - **page.tsx**: `primary_color`/`accent_color` валидируются regex (`hex` / `rgb()` / `hsl()`) перед `setProperty` — невалидный цвет не применяется (остаётся дефолт), защита от CSS-инъекции подменённым бренд-конфигом. - Happy-path (валидный бренд praktika: лого + цвета) не тронут. ## Why Бренд-поля рендерились без санитизации; в чейне с `?brand=` override (#765) подменённый бренд был вектором для `javascript:`-URL в логотипе / CSS-инъекции в цветах. ## Test plan - [x] `npx tsc --noEmit` → 0 ошибок - [x] `npm run build` → ✓ Compiled successfully, 10/10 pages - [x] `grep safeUrl Topbar.tsx` → ≥1; `grep 'test(' page.tsx` → 2 (оба цвета под guard) - [ ] Smoke: praktika-бренд рендерит лого+цвета как сейчас; `javascript:`-logo → TI-марка (qa) Refs #766
bot-frontend added 1 commit 2026-05-30 16:53:20 +00:00
#766 (security, чейн с #765): brand-поля рендерились без проверки.
- Topbar: logo_url → safeUrl (блок javascript:/data: схем); невалидный URL →
  fallback на TI-марку (без broken/опасного img), brand.name сохраняется.
- page.tsx: primary/accent_color под regex-guard (hex/rgb/hsl) перед setProperty;
  невалидный цвет → дефолт (защита от CSS-инъекции подменённым брендом).
Happy-path (praktika) не тронут.

Refs #766
bot-reviewer approved these changes 2026-05-30 16:58:00 +00:00
bot-reviewer left a comment
Collaborator

APPROVE

Frontend brand-санитизация (security, чейн #765/#766, frontend-двойник #789 part D).

Correctness

  • Topbar: brand.logo_urlsafeUrl(...); валидный → img, иначе fallback <span>TI</span> (brand.name сохранён). Блок javascript:/data:/non-http(s).
  • page.tsx: COLOR_RE = /^#[0-9a-fA-F]{3,8}$|^(rgb|hsl)a?\([\d.,%\s/]+\)$/ гейтит setProperty; char class исключает ;{}: → CSS-инъекция (напр. red;}body{…) не матчится. Только hex + rgb/hsl(a). Невалид → дефолт. Корректно.
  • tsc 0, build ✓ 10/10. Happy-path (praktika) сохранён. DoD выполнен (safeUrl grep, test() guard).

Advisory (non-blocking) — (1) safeUrl(brand.logo_url) вызывается дважды в Topbar (косметика, чистая fn); (2) frontend использует только scheme-block vs domain-allowlist в PDF #789 — достаточно для client-side img XSS (нет SSRF в браузере), domain-allowlist совпал бы со строгостью PDF (опц. follow-up).

Scope/security — page.tsx + Topbar.tsx; переиспользует existing safeUrl, нет backend, нет secrets, нет self-extending триггера. P3.

Verdict: APPROVE (#766). Smoke (praktika logo+цвета; javascript:-logo → TI) → qa.

<!-- gendesign-review-bot: sha=639ab3f verdict=approve --> ✅ APPROVE Frontend brand-санитизация (security, чейн #765/#766, frontend-двойник #789 part D). **Correctness** - Topbar: `brand.logo_url` → `safeUrl(...)`; валидный → img, иначе fallback `<span>TI</span>` (brand.name сохранён). Блок `javascript:`/`data:`/non-http(s). - page.tsx: `COLOR_RE = /^#[0-9a-fA-F]{3,8}$|^(rgb|hsl)a?\([\d.,%\s/]+\)$/` гейтит `setProperty`; char class исключает `;{}:` → CSS-инъекция (напр. `red;}body{…`) не матчится. Только hex + rgb/hsl(a). Невалид → дефолт. Корректно. - tsc 0, build ✓ 10/10. Happy-path (praktika) сохранён. DoD выполнен (safeUrl grep, `test()` guard). **Advisory (non-blocking)** — (1) `safeUrl(brand.logo_url)` вызывается дважды в Topbar (косметика, чистая fn); (2) frontend использует только scheme-block vs domain-allowlist в PDF #789 — достаточно для client-side img XSS (нет SSRF в браузере), domain-allowlist совпал бы со строгостью PDF (опц. follow-up). **Scope/security** — page.tsx + Topbar.tsx; переиспользует existing safeUrl, нет backend, нет secrets, нет self-extending триггера. P3. **Verdict: ✅ APPROVE** (#766). Smoke (praktika logo+цвета; javascript:-logo → TI) → qa.
bot-reviewer merged commit b175ea4225 into main 2026-05-30 16:58:14 +00:00
bot-reviewer deleted branch feat/766-brand-sanitize 2026-05-30 16:58:14 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#791
No description provided.