Fix HTML encoding in templating (#404)
This commit is contained in:
parent
7e07ca3df1
commit
322aa97a18
11
src/post.rs
11
src/post.rs
@ -1,6 +1,5 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::client::json;
|
use crate::client::json;
|
||||||
use crate::esc;
|
|
||||||
use crate::server::RequestExt;
|
use crate::server::RequestExt;
|
||||||
use crate::subreddit::{can_access_quarantine, quarantine};
|
use crate::subreddit::{can_access_quarantine, quarantine};
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
@ -13,7 +12,7 @@ use std::collections::HashSet;
|
|||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "post.html", escape = "none")]
|
#[template(path = "post.html")]
|
||||||
struct PostTemplate {
|
struct PostTemplate {
|
||||||
comments: Vec<Comment>,
|
comments: Vec<Comment>,
|
||||||
post: Post,
|
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
|
// Build a post using data parsed from Reddit post API
|
||||||
Post {
|
Post {
|
||||||
id: val(post, "id"),
|
id: val(post, "id"),
|
||||||
title: esc!(post, "title"),
|
title: val(post, "title"),
|
||||||
community: val(post, "subreddit"),
|
community: val(post, "subreddit"),
|
||||||
body,
|
body,
|
||||||
author: Author {
|
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_richtext"].as_array(),
|
||||||
post["data"]["author_flair_text"].as_str(),
|
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"),
|
background_color: val(post, "author_flair_background_color"),
|
||||||
foreground_color: val(post, "author_flair_text_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_richtext"].as_array(),
|
||||||
post["data"]["link_flair_text"].as_str(),
|
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"),
|
background_color: val(post, "link_flair_background_color"),
|
||||||
foreground_color: if val(post, "link_flair_text_color") == "dark" {
|
foreground_color: if val(post, "link_flair_text_color") == "dark" {
|
||||||
"black".to_string()
|
"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_richtext"].as_array(),
|
||||||
data["author_flair_text"].as_str(),
|
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"),
|
background_color: val(&comment, "author_flair_background_color"),
|
||||||
foreground_color: val(&comment, "author_flair_text_color"),
|
foreground_color: val(&comment, "author_flair_text_color"),
|
||||||
},
|
},
|
||||||
|
@ -29,7 +29,7 @@ struct Subreddit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "search.html", escape = "none")]
|
#[template(path = "search.html")]
|
||||||
struct SearchTemplate {
|
struct SearchTemplate {
|
||||||
posts: Vec<Post>,
|
posts: Vec<Post>,
|
||||||
subreddits: Vec<Subreddit>,
|
subreddits: Vec<Subreddit>,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::esc;
|
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, rewrite_urls, setting, template, val, Post, Preferences, Subreddit,
|
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
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "subreddit.html", escape = "none")]
|
#[template(path = "subreddit.html")]
|
||||||
struct SubredditTemplate {
|
struct SubredditTemplate {
|
||||||
sub: Subreddit,
|
sub: Subreddit,
|
||||||
posts: Vec<Post>,
|
posts: Vec<Post>,
|
||||||
@ -28,7 +27,7 @@ struct SubredditTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "wiki.html", escape = "none")]
|
#[template(path = "wiki.html")]
|
||||||
struct WikiTemplate {
|
struct WikiTemplate {
|
||||||
sub: String,
|
sub: String,
|
||||||
wiki: String,
|
wiki: String,
|
||||||
@ -38,7 +37,7 @@ struct WikiTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "wall.html", escape = "none")]
|
#[template(path = "wall.html")]
|
||||||
struct WallTemplate {
|
struct WallTemplate {
|
||||||
title: String,
|
title: String,
|
||||||
sub: String,
|
sub: String,
|
||||||
@ -408,9 +407,9 @@ async fn subreddit(sub: &str, quarantined: bool) -> Result<Subreddit, String> {
|
|||||||
let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() };
|
let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() };
|
||||||
|
|
||||||
Ok(Subreddit {
|
Ok(Subreddit {
|
||||||
name: esc!(&res, "display_name"),
|
name: val(&res, "display_name"),
|
||||||
title: esc!(&res, "title"),
|
title: val(&res, "title"),
|
||||||
description: esc!(&res, "public_description"),
|
description: val(&res, "public_description"),
|
||||||
info: rewrite_urls(&val(&res, "description_html")),
|
info: rewrite_urls(&val(&res, "description_html")),
|
||||||
// moderators: moderators_list(sub, quarantined).await.unwrap_or_default(),
|
// moderators: moderators_list(sub, quarantined).await.unwrap_or_default(),
|
||||||
icon: format_url(&icon),
|
icon: format_url(&icon),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// CRATES
|
// CRATES
|
||||||
use crate::client::json;
|
use crate::client::json;
|
||||||
use crate::esc;
|
|
||||||
use crate::server::RequestExt;
|
use crate::server::RequestExt;
|
||||||
use crate::utils::{error, filter_posts, format_url, get_filters, param, template, Post, Preferences, User};
|
use crate::utils::{error, filter_posts, format_url, get_filters, param, template, Post, Preferences, User};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
@ -9,7 +8,7 @@ use time::{macros::format_description, OffsetDateTime};
|
|||||||
|
|
||||||
// STRUCTS
|
// STRUCTS
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "user.html", escape = "none")]
|
#[template(path = "user.html")]
|
||||||
struct UserTemplate {
|
struct UserTemplate {
|
||||||
user: User,
|
user: User,
|
||||||
posts: Vec<Post>,
|
posts: Vec<Post>,
|
||||||
@ -102,11 +101,11 @@ async fn user(name: &str) -> Result<User, String> {
|
|||||||
// Parse the JSON output into a User struct
|
// Parse the JSON output into a User struct
|
||||||
User {
|
User {
|
||||||
name: res["data"]["name"].as_str().unwrap_or(name).to_owned(),
|
name: res["data"]["name"].as_str().unwrap_or(name).to_owned(),
|
||||||
title: esc!(about("title")),
|
title: about("title"),
|
||||||
icon: format_url(&about("icon_img")),
|
icon: format_url(&about("icon_img")),
|
||||||
karma: res["data"]["total_karma"].as_i64().unwrap_or(0),
|
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(),
|
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"),
|
description: about("public_description"),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
27
src/utils.rs
27
src/utils.rs
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// CRATES
|
// CRATES
|
||||||
//
|
//
|
||||||
use crate::{client::json, esc, server::RequestExt};
|
use crate::{client::json, server::RequestExt};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
use cookie::Cookie;
|
use cookie::Cookie;
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Body, Request, Response};
|
||||||
@ -42,7 +42,7 @@ impl FlairPart {
|
|||||||
Self {
|
Self {
|
||||||
flair_part_type: value("e").to_string(),
|
flair_part_type: value("e").to_string(),
|
||||||
value: match value("e") {
|
value: match value("e") {
|
||||||
"text" => esc!(value("t")),
|
"text" => value("t").to_string(),
|
||||||
"emoji" => format_url(value("u")),
|
"emoji" => format_url(value("u")),
|
||||||
_ => String::new(),
|
_ => String::new(),
|
||||||
},
|
},
|
||||||
@ -55,7 +55,7 @@ impl FlairPart {
|
|||||||
"text" => match text_flair {
|
"text" => match text_flair {
|
||||||
Some(text) => vec![Self {
|
Some(text) => vec![Self {
|
||||||
flair_part_type: "text".to_string(),
|
flair_part_type: "text".to_string(),
|
||||||
value: esc!(text),
|
value: text.to_string(),
|
||||||
}],
|
}],
|
||||||
None => Vec::new(),
|
None => Vec::new(),
|
||||||
},
|
},
|
||||||
@ -241,7 +241,7 @@ impl Post {
|
|||||||
let (rel_time, created) = time(data["created_utc"].as_f64().unwrap_or_default());
|
let (rel_time, created) = time(data["created_utc"].as_f64().unwrap_or_default());
|
||||||
let score = data["score"].as_i64().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 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
|
// Determine the type of media along with the media URL
|
||||||
let (post_type, media, gallery) = Media::parse(data).await;
|
let (post_type, media, gallery) = Media::parse(data).await;
|
||||||
@ -266,7 +266,7 @@ impl Post {
|
|||||||
data["author_flair_richtext"].as_array(),
|
data["author_flair_richtext"].as_array(),
|
||||||
data["author_flair_text"].as_str(),
|
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"),
|
background_color: val(post, "author_flair_background_color"),
|
||||||
foreground_color: val(post, "author_flair_text_color"),
|
foreground_color: val(post, "author_flair_text_color"),
|
||||||
},
|
},
|
||||||
@ -294,7 +294,7 @@ impl Post {
|
|||||||
data["link_flair_richtext"].as_array(),
|
data["link_flair_richtext"].as_array(),
|
||||||
data["link_flair_text"].as_str(),
|
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"),
|
background_color: val(post, "link_flair_background_color"),
|
||||||
foreground_color: if val(post, "link_flair_text_color") == "dark" {
|
foreground_color: if val(post, "link_flair_text_color") == "dark" {
|
||||||
"black".to_string()
|
"black".to_string()
|
||||||
@ -320,7 +320,7 @@ impl Post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "comment.html", escape = "none")]
|
#[template(path = "comment.html")]
|
||||||
// Comment with content, post, score and data/time that it was posted
|
// Comment with content, post, score and data/time that it was posted
|
||||||
pub struct Comment {
|
pub struct Comment {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@ -396,7 +396,7 @@ impl Awards {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "error.html", escape = "none")]
|
#[template(path = "error.html")]
|
||||||
pub struct ErrorTemplate {
|
pub struct ErrorTemplate {
|
||||||
pub msg: String,
|
pub msg: String,
|
||||||
pub prefs: Preferences,
|
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()
|
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
|
// NETWORKING
|
||||||
//
|
//
|
||||||
|
@ -32,9 +32,9 @@
|
|||||||
{% if is_filtered %}
|
{% if is_filtered %}
|
||||||
<div class="comment_body_filtered {% if highlighted %}highlighted{% endif %}">(Filtered content)</div>
|
<div class="comment_body_filtered {% if highlighted %}highlighted{% endif %}">(Filtered content)</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="comment_body {% if highlighted %}highlighted{% endif %}">{{ body }}</div>
|
<div class="comment_body {% if highlighted %}highlighted{% endif %}">{{ body|safe }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<blockquote class="replies">{% for c in replies -%}{{ c.render().unwrap() }}{%- endfor %}
|
<blockquote class="replies">{% for c in replies -%}{{ c.render().unwrap()|safe }}{%- endfor %}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
@ -110,7 +110,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- POST BODY -->
|
<!-- POST BODY -->
|
||||||
<div class="post_body">{{ post.body }}</div>
|
<div class="post_body">{{ post.body|safe }}</div>
|
||||||
<div class="post_score" title="{{ post.score.1 }}">{{ post.score.0 }}<span class="label"> Upvotes</span></div>
|
<div class="post_score" title="{{ post.score.1 }}">{{ post.score.0 }}<span class="label"> Upvotes</span></div>
|
||||||
<div class="post_footer">
|
<div class="post_footer">
|
||||||
<ul id="post_links">
|
<ul id="post_links">
|
||||||
@ -144,7 +144,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{{ c.render().unwrap() }}
|
{{ c.render().unwrap()|safe }}
|
||||||
</div>
|
</div>
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% for subreddit in subreddits %}
|
{% for subreddit in subreddits %}
|
||||||
<a href="{{ subreddit.url }}" class="search_subreddit">
|
<a href="{{ subreddit.url }}" class="search_subreddit">
|
||||||
<div class="search_subreddit_left">{% if subreddit.icon != "" %}<img loading="lazy" src="{{ subreddit.icon }}" alt="r/{{ subreddit.name }} icon">{% endif %}</div>
|
<div class="search_subreddit_left">{% if subreddit.icon != "" %}<img loading="lazy" src="{{ subreddit.icon|safe }}" alt="r/{{ subreddit.name }} icon">{% endif %}</div>
|
||||||
<div class="search_subreddit_right">
|
<div class="search_subreddit_right">
|
||||||
<p class="search_subreddit_header">
|
<p class="search_subreddit_header">
|
||||||
<span class="search_subreddit_name">r/{{ subreddit.name }}</span>
|
<span class="search_subreddit_name">r/{{ subreddit.name }}</span>
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
<details class="panel" id="sidebar">
|
<details class="panel" id="sidebar">
|
||||||
<summary id="sidebar_label">Sidebar</summary>
|
<summary id="sidebar_label">Sidebar</summary>
|
||||||
<div id="sidebar_contents">
|
<div id="sidebar_contents">
|
||||||
{{ sub.info }}
|
{{ sub.info|safe }}
|
||||||
{# <hr>
|
{# <hr>
|
||||||
<h2>Moderators</h2>
|
<h2>Moderators</h2>
|
||||||
<br>
|
<br>
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
<a class="comment_link" href="{{ post.permalink }}">COMMENT</a>
|
<a class="comment_link" href="{{ post.permalink }}">COMMENT</a>
|
||||||
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
<span class="created" title="{{ post.created }}">{{ post.rel_time }}</span>
|
||||||
</summary>
|
</summary>
|
||||||
<p class="comment_body">{{ post.body }}</p>
|
<p class="comment_body">{{ post.body|safe }}</p>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -138,7 +138,7 @@
|
|||||||
|
|
||||||
<div class="post_score" title="{{ post.score.1 }}">{{ post.score.0 }}<span class="label"> Upvotes</span></div>
|
<div class="post_score" title="{{ post.score.1 }}">{{ post.score.0 }}<span class="label"> Upvotes</span></div>
|
||||||
<div class="post_body post_preview">
|
<div class="post_body post_preview">
|
||||||
{{ post.body }}
|
{{ post.body|safe }}
|
||||||
</div>
|
</div>
|
||||||
<div class="post_footer">
|
<div class="post_footer">
|
||||||
<a href="{{ post.permalink }}" class="post_comments" title="{{ post.comments.1 }} comments">{{ post.comments.0 }} comments</a>
|
<a href="{{ post.permalink }}" class="post_comments" title="{{ post.comments.1 }} comments">{{ post.comments.0 }} comments</a>
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<div>Wiki</div>
|
<div>Wiki</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="wiki">
|
<div id="wiki">
|
||||||
{{ wiki }}
|
{{ wiki|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
Loading…
Reference in New Issue
Block a user