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:
2026-09-18 14:23:48 +00:00
parent 3b00e2721a
commit 9b2537761f
2 changed files with 18 additions and 6 deletions

View File

@@ -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 {