From 322aa97a1813fd550bf4d3f91997055b45615fd8 Mon Sep 17 00:00:00 2001 From: Nick Lowery Date: Fri, 20 May 2022 23:28:31 -0600 Subject: [PATCH] Fix HTML encoding in templating (#404) --- src/post.rs | 11 +++++------ src/search.rs | 2 +- src/subreddit.rs | 13 ++++++------- src/user.rs | 7 +++---- src/utils.rs | 27 ++++++++------------------- templates/comment.html | 4 ++-- templates/post.html | 4 ++-- templates/search.html | 2 +- templates/subreddit.html | 2 +- templates/user.html | 2 +- templates/utils.html | 2 +- templates/wiki.html | 4 ++-- 12 files changed, 33 insertions(+), 47 deletions(-) diff --git a/src/post.rs b/src/post.rs index f9d6e74..02ca380 100644 --- a/src/post.rs +++ b/src/post.rs @@ -1,6 +1,5 @@ // CRATES use crate::client::json; -use crate::esc; use crate::server::RequestExt; use crate::subreddit::{can_access_quarantine, quarantine}; use crate::utils::{ @@ -13,7 +12,7 @@ use std::collections::HashSet; // STRUCTS #[derive(Template)] -#[template(path = "post.html", escape = "none")] +#[template(path = "post.html")] struct PostTemplate { comments: Vec, post: Post, @@ -111,7 +110,7 @@ async fn parse_post(json: &serde_json::Value) -> Post { // Build a post using data parsed from Reddit post API Post { id: val(post, "id"), - title: esc!(post, "title"), + title: val(post, "title"), community: val(post, "subreddit"), body, author: Author { @@ -122,7 +121,7 @@ async fn parse_post(json: &serde_json::Value) -> Post { post["data"]["author_flair_richtext"].as_array(), post["data"]["author_flair_text"].as_str(), ), - text: esc!(post, "link_flair_text"), + text: val(post, "link_flair_text"), background_color: val(post, "author_flair_background_color"), foreground_color: val(post, "author_flair_text_color"), }, @@ -146,7 +145,7 @@ async fn parse_post(json: &serde_json::Value) -> Post { post["data"]["link_flair_richtext"].as_array(), post["data"]["link_flair_text"].as_str(), ), - text: esc!(post, "link_flair_text"), + text: val(post, "link_flair_text"), background_color: val(post, "link_flair_background_color"), foreground_color: if val(post, "link_flair_text_color") == "dark" { "black".to_string() @@ -218,7 +217,7 @@ fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str, data["author_flair_richtext"].as_array(), data["author_flair_text"].as_str(), ), - text: esc!(&comment, "link_flair_text"), + text: val(&comment, "link_flair_text"), background_color: val(&comment, "author_flair_background_color"), foreground_color: val(&comment, "author_flair_text_color"), }, diff --git a/src/search.rs b/src/search.rs index 3547679..4dd3b5f 100644 --- a/src/search.rs +++ b/src/search.rs @@ -29,7 +29,7 @@ struct Subreddit { } #[derive(Template)] -#[template(path = "search.html", escape = "none")] +#[template(path = "search.html")] struct SearchTemplate { posts: Vec, subreddits: Vec, diff --git a/src/subreddit.rs b/src/subreddit.rs index fcd0d2a..b03e7e9 100644 --- a/src/subreddit.rs +++ b/src/subreddit.rs @@ -1,5 +1,4 @@ // CRATES -use crate::esc; use crate::utils::{ catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, rewrite_urls, setting, template, val, Post, Preferences, Subreddit, }; @@ -11,7 +10,7 @@ use time::{Duration, OffsetDateTime}; // STRUCTS #[derive(Template)] -#[template(path = "subreddit.html", escape = "none")] +#[template(path = "subreddit.html")] struct SubredditTemplate { sub: Subreddit, posts: Vec, @@ -28,7 +27,7 @@ struct SubredditTemplate { } #[derive(Template)] -#[template(path = "wiki.html", escape = "none")] +#[template(path = "wiki.html")] struct WikiTemplate { sub: String, wiki: String, @@ -38,7 +37,7 @@ struct WikiTemplate { } #[derive(Template)] -#[template(path = "wall.html", escape = "none")] +#[template(path = "wall.html")] struct WallTemplate { title: String, sub: String, @@ -408,9 +407,9 @@ async fn subreddit(sub: &str, quarantined: bool) -> Result { let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() }; Ok(Subreddit { - name: esc!(&res, "display_name"), - title: esc!(&res, "title"), - description: esc!(&res, "public_description"), + name: val(&res, "display_name"), + title: val(&res, "title"), + description: val(&res, "public_description"), info: rewrite_urls(&val(&res, "description_html")), // moderators: moderators_list(sub, quarantined).await.unwrap_or_default(), icon: format_url(&icon), diff --git a/src/user.rs b/src/user.rs index f83ee98..69c4ae9 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,6 +1,5 @@ // CRATES use crate::client::json; -use crate::esc; use crate::server::RequestExt; use crate::utils::{error, filter_posts, format_url, get_filters, param, template, Post, Preferences, User}; use askama::Template; @@ -9,7 +8,7 @@ use time::{macros::format_description, OffsetDateTime}; // STRUCTS #[derive(Template)] -#[template(path = "user.html", escape = "none")] +#[template(path = "user.html")] struct UserTemplate { user: User, posts: Vec, @@ -102,11 +101,11 @@ async fn user(name: &str) -> Result { // Parse the JSON output into a User struct User { name: res["data"]["name"].as_str().unwrap_or(name).to_owned(), - title: esc!(about("title")), + title: about("title"), icon: format_url(&about("icon_img")), karma: res["data"]["total_karma"].as_i64().unwrap_or(0), created: created.format(format_description!("[month repr:short] [day] '[year repr:last_two]")).unwrap_or_default(), - banner: esc!(about("banner_img")), + banner: about("banner_img"), description: about("public_description"), } }) diff --git a/src/utils.rs b/src/utils.rs index 60afc6f..2691d16 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,7 @@ // // CRATES // -use crate::{client::json, esc, server::RequestExt}; +use crate::{client::json, server::RequestExt}; use askama::Template; use cookie::Cookie; use hyper::{Body, Request, Response}; @@ -42,7 +42,7 @@ impl FlairPart { Self { flair_part_type: value("e").to_string(), value: match value("e") { - "text" => esc!(value("t")), + "text" => value("t").to_string(), "emoji" => format_url(value("u")), _ => String::new(), }, @@ -55,7 +55,7 @@ impl FlairPart { "text" => match text_flair { Some(text) => vec![Self { flair_part_type: "text".to_string(), - value: esc!(text), + value: text.to_string(), }], None => Vec::new(), }, @@ -241,7 +241,7 @@ impl Post { let (rel_time, created) = time(data["created_utc"].as_f64().unwrap_or_default()); let score = data["score"].as_i64().unwrap_or_default(); let ratio: f64 = data["upvote_ratio"].as_f64().unwrap_or(1.0) * 100.0; - let title = esc!(post, "title"); + let title = val(post, "title"); // Determine the type of media along with the media URL let (post_type, media, gallery) = Media::parse(data).await; @@ -266,7 +266,7 @@ impl Post { data["author_flair_richtext"].as_array(), data["author_flair_text"].as_str(), ), - text: esc!(post, "link_flair_text"), + text: val(post, "link_flair_text"), background_color: val(post, "author_flair_background_color"), foreground_color: val(post, "author_flair_text_color"), }, @@ -294,7 +294,7 @@ impl Post { data["link_flair_richtext"].as_array(), data["link_flair_text"].as_str(), ), - text: esc!(post, "link_flair_text"), + text: val(post, "link_flair_text"), background_color: val(post, "link_flair_background_color"), foreground_color: if val(post, "link_flair_text_color") == "dark" { "black".to_string() @@ -320,7 +320,7 @@ impl Post { } #[derive(Template)] -#[template(path = "comment.html", escape = "none")] +#[template(path = "comment.html")] // Comment with content, post, score and data/time that it was posted pub struct Comment { pub id: String, @@ -396,7 +396,7 @@ impl Awards { } #[derive(Template)] -#[template(path = "error.html", escape = "none")] +#[template(path = "error.html")] pub struct ErrorTemplate { pub msg: String, pub prefs: Preferences, @@ -678,17 +678,6 @@ pub fn val(j: &Value, k: &str) -> String { j["data"][k].as_str().unwrap_or_default().to_string() } -// Escape < and > to accurately render HTML -#[macro_export] -macro_rules! esc { - ($f:expr) => { - $f.replace('&', "&").replace('<', "<").replace('>', ">") - }; - ($j:expr, $k:expr) => { - $j["data"][$k].as_str().unwrap_or_default().to_string().replace('<', "<").replace('>', ">") - }; -} - // // NETWORKING // diff --git a/templates/comment.html b/templates/comment.html index 3014192..61c44f5 100644 --- a/templates/comment.html +++ b/templates/comment.html @@ -32,9 +32,9 @@ {% if is_filtered %}
(Filtered content)
{% else %} -
{{ body }}
+
{{ body|safe }}
{% endif %} -
{% for c in replies -%}{{ c.render().unwrap() }}{%- endfor %} +
{% for c in replies -%}{{ c.render().unwrap()|safe }}{%- endfor %}
diff --git a/templates/post.html b/templates/post.html index add5881..97715ec 100644 --- a/templates/post.html +++ b/templates/post.html @@ -110,7 +110,7 @@ {% endif %} -
{{ post.body }}
+
{{ post.body|safe }}
{{ post.score.0 }} Upvotes
{%- endfor %} diff --git a/templates/search.html b/templates/search.html index 0218c35..c5cff86 100644 --- a/templates/search.html +++ b/templates/search.html @@ -39,7 +39,7 @@ {% endif %} {% for subreddit in subreddits %} -
{% if subreddit.icon != "" %}r/{{ subreddit.name }} icon{% endif %}
+
{% if subreddit.icon != "" %}r/{{ subreddit.name }} icon{% endif %}
{% endif %} diff --git a/templates/utils.html b/templates/utils.html index a11583f..0e22900 100644 --- a/templates/utils.html +++ b/templates/utils.html @@ -138,7 +138,7 @@
{{ post.score.0 }} Upvotes
- {{ post.body }} + {{ post.body|safe }}
- {{ wiki }} + {{ wiki|safe }}
-{% endblock %} \ No newline at end of file +{% endblock %}