fix(docs): block javascript: URLs in renderMarkdown href (XSS guard) #131

Merged
lekss361 merged 1 commit from fix/render-markdown-url-validation-130 into main 2026-05-14 20:39:47 +00:00
lekss361 commented 2026-05-14 20:34:17 +00:00 (Migrated from github.com)

Summary

Per audit batch #127 P2 security: renderMarkdown substituted [text](url)<a href="$2"> verbatim без URL validation. [click](javascript:alert(1)) → executable XSS.

Currently low risk (markdown source — build-time fs.readFileSync от public/docs/), но защита-в-глубину.

Fix

frontend/src/app/docs/b2b-channels/renderMarkdown.ts:

  • New safeUrl() — allowlist https://, http://, /, #, mailto:. Else "#".
  • Link regex replacement → callback (URL → safeUrl → re-escape → href)

Test matrix verified

Input href
[ok](https://example.com) https://example.com
[ok](/local) /local
[bad](javascript:alert(1)) #
[bad](data:text/html,...) #
[bad](vbscript:msgbox(1)) #

Checks

  • tsc: 0 errors
  • lint: 0 warnings

Vault

fixes/Bug_RenderMarkdown_JavascriptUrl_May14.md

Closes #130
Refs: #127

## Summary Per audit batch #127 P2 security: `renderMarkdown` substituted `[text](url)` → `<a href="$2">` verbatim без URL validation. `[click](javascript:alert(1))` → executable XSS. Currently low risk (markdown source — build-time `fs.readFileSync` от `public/docs/`), но **защита-в-глубину**. ## Fix `frontend/src/app/docs/b2b-channels/renderMarkdown.ts`: - New `safeUrl()` — allowlist `https://`, `http://`, `/`, `#`, `mailto:`. Else `"#"`. - Link regex replacement → callback (URL → safeUrl → re-escape → href) ## Test matrix verified | Input | href | |---|---| | `[ok](https://example.com)` | `https://example.com` | | `[ok](/local)` | `/local` | | `[bad](javascript:alert(1))` | `#` | | `[bad](data:text/html,...)` | `#` | | `[bad](vbscript:msgbox(1))` | `#` | ## Checks - tsc: 0 errors - lint: 0 warnings ## Vault `fixes/Bug_RenderMarkdown_JavascriptUrl_May14.md` Closes #130 Refs: #127
lekss361 commented 2026-05-14 20:39:30 +00:00 (Migrated from github.com)

Bot review (SHA 6186804, CI )

🔒 Корректный XSS guard, defence-in-depth:

Allowlist (https://, http://, /, #, mailto:) — правильнее чем blocklist, не упустит будущие vectors
Unescape перед проверкой — учитывает что URL уже HTML-escaped после escapeHtml(line); иначе javascript&#58;... или &amp; в URL ломали бы scheme matching
Re-escape через escapeHtml(safeUrl(...)) — финальный href корректно экранирован
Edge cases все обрабатываются:

  • Case JavaScript: → не матчит allowlist → #
  • Leading whitespace → trim() убирает
  • URL-encoded colon javascript%3a... → нет литерального : для scheme → trailing string, no allowlist match → #
  • Attribute injection через " → escapeHtml re-escapes в &quot;
    Closes #130 и Refs: #127 (audit batch)

Минор (не блокер, pre-existing)

Link regex \[([^\]]+)\]\(([^)]+)\) обрезается на первом ) в URL. Это стандартное markdown поведение — URL с скобками должны быть %29-кодированы. Не введено этим PR.

approve merge

## Bot review (SHA `6186804`, CI ✅) 🔒 Корректный XSS guard, defence-in-depth: ✅ **Allowlist** (https://, http://, /, #, mailto:) — правильнее чем blocklist, не упустит будущие vectors ✅ **Unescape перед проверкой** — учитывает что URL уже HTML-escaped после `escapeHtml(line)`; иначе `javascript&#58;...` или `&amp;` в URL ломали бы scheme matching ✅ **Re-escape через `escapeHtml(safeUrl(...))`** — финальный href корректно экранирован ✅ **Edge cases все обрабатываются**: - Case `JavaScript:` → не матчит allowlist → `#` - Leading whitespace → trim() убирает - URL-encoded colon `javascript%3a...` → нет литерального `:` для scheme → trailing string, no allowlist match → `#` - Attribute injection через `"` → escapeHtml re-escapes в `&quot;` ✅ `Closes #130` и `Refs: #127` (audit batch) ### Минор (не блокер, pre-existing) Link regex `\[([^\]]+)\]\(([^)]+)\)` обрезается на первом `)` в URL. Это стандартное markdown поведение — URL с скобками должны быть %29-кодированы. Не введено этим PR. approve merge
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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#131
No description provided.