Ask for a post's images without a referrer
jeffgeerling.com answers 403 to an image request whose Referer is another site, so his posts showed a broken image on iOS and the alt text on desktop. The sanitiser now gives every <img> referrerpolicy="no-referrer". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
22
src/web.rs
22
src/web.rs
@@ -858,12 +858,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn a_relative_image_resolves_against_the_post() {
|
||||
let mut b = ammonia::Builder::new();
|
||||
let mut b = feed_sanitizer();
|
||||
let html = r#"<img src="images/a.webp"><a href="/about">x</a><script>bad()</script>"#;
|
||||
let out = clean_description(&mut b, html, Some("https://example.com/2026/04/post/"));
|
||||
assert!(out.contains(r#"src="https://example.com/2026/04/post/images/a.webp""#), "{out}");
|
||||
assert!(out.contains(r#"href="https://example.com/about""#), "{out}");
|
||||
assert!(!out.contains("bad()"), "{out}");
|
||||
assert!(out.contains(r#"referrerpolicy="no-referrer""#), "{out}");
|
||||
assert!(clean_description(&mut b, html, None).contains(r#"src="images/a.webp""#));
|
||||
}
|
||||
|
||||
@@ -1046,11 +1047,7 @@ fn entry_page(
|
||||
);
|
||||
let mut rows =
|
||||
db.entries_in(user_id, feed, filter, search, page.offset, page.limit.clamp(1, 200), &order)?;
|
||||
// Feed HTML is untrusted: it reaches the page only after ammonia has been through it.
|
||||
// Every link opens in a new tab -- ammonia's default rel="noopener noreferrer" already
|
||||
// keeps that safe -- so following one in show notes never navigates away from ipx.
|
||||
let mut sanitizer = ammonia::Builder::new();
|
||||
sanitizer.add_tag_attributes("a", &["target"]).set_tag_attribute_value("a", "target", "_blank");
|
||||
let mut sanitizer = feed_sanitizer();
|
||||
for row in &mut rows {
|
||||
if let Some(d) = &row.description {
|
||||
row.description = Some(clean_description(&mut sanitizer, d, row.link.as_deref()));
|
||||
@@ -1060,6 +1057,19 @@ fn entry_page(
|
||||
Ok(Json(EntryPage { total, entries: rows }))
|
||||
}
|
||||
|
||||
/// Feed HTML is untrusted: it reaches the page only after ammonia has been through it.
|
||||
fn feed_sanitizer() -> ammonia::Builder<'static> {
|
||||
let mut b = ammonia::Builder::new();
|
||||
// Every link opens in a new tab -- ammonia's default rel="noopener noreferrer" already
|
||||
// keeps that safe -- so following one in show notes never navigates away from ipx.
|
||||
b.add_tag_attributes("a", &["target"]).set_tag_attribute_value("a", "target", "_blank");
|
||||
// An image is asked for without saying it is shown on ipx. A site that refuses images to
|
||||
// other sites' pages goes by that: jeffgeerling.com answers 403, and his posts showed a
|
||||
// broken image on iOS and only the alt text on desktop.
|
||||
b.add_tag_attributes("img", &["referrerpolicy"]).set_tag_attribute_value("img", "referrerpolicy", "no-referrer");
|
||||
b
|
||||
}
|
||||
|
||||
/// A relative `src` or `href` in a post means relative to the post, not to ipx: The
|
||||
/// Observation Deck's `images/37k-a-day-bro.webp` came up as a broken image.
|
||||
fn clean_description(sanitizer: &mut ammonia::Builder, html: &str, link: Option<&str>) -> String {
|
||||
|
||||
Reference in New Issue
Block a user