diff --git a/src/content.rs b/src/content.rs index f79a4ff..db3b2c2 100644 --- a/src/content.rs +++ b/src/content.rs @@ -199,6 +199,18 @@ pub fn render_inline_markdown(text: &str) -> String { in_link -= 1; None } + // Images take the same gate as links: https or same-origin + // only - no data:, no plain http. + Event::Start(Tag::Image { dest_url, .. }) + if !(dest_url.starts_with("https://") || dest_url.starts_with('/')) => + { + in_link += 1; + None + } + Event::End(TagEnd::Image) if in_link > 0 => { + in_link -= 1; + None + } other => Some(other), }); let mut out = String::new(); @@ -2026,6 +2038,15 @@ alternatives: fn markdown_drops_html_and_unsafe_links() { assert_eq!(render_inline_markdown("x z"), "x y z"); assert_eq!(render_inline_markdown("[bad](javascript:alert(1))"), "bad"); + assert_eq!( + render_inline_markdown("![site](https://x.no/a.jpg)"), + "\"site\"" + ); + assert_eq!(render_inline_markdown("![x](data:image/png;base64,AA)"), ""); + assert_eq!( + render_inline_markdown("![local](/images/a.jpg)"), + "\"local\"" + ); assert_eq!(render_inline_markdown("[ok](/shape)"), "ok"); assert_eq!(render_inline_markdown("[mail](mailto:bl@uhhm.no)"), "mail"); } diff --git a/style/main.css b/style/main.css index 0d7ba6a..8e2babe 100644 --- a/style/main.css +++ b/style/main.css @@ -536,6 +536,18 @@ main.not-found { color: var(--accent); } +/* images inside markdown descriptions: full-width figures in the + card's flow, framed like the rest of the press sheet */ +.alt-description img, +.item-card-description img, +.feature p img { + display: block; + width: 100%; + margin: 0.6rem 0 0.2rem; + border-radius: 0.5rem; + border: 0.06rem solid var(--line); +} + .alt-description code, .feature p code { font-size: 0.9em;