Skip to content

Commit 6323cee

Browse files
committed
Replace PossibleTyposquatEmail struct with EmailMessage::from_template()
1 parent c619862 commit 6323cee

File tree

3 files changed

+41
-44
lines changed

3 files changed

+41
-44
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
New crate {{ crate_name }} may be typosquatting one or more other crates.
2+
3+
Visit https://{{ domain }}/crates/{{ crate_name }} to see the offending crate.
4+
5+
Specific squat checks that triggered:
6+
7+
{% for squat in squats -%}
8+
- {{ squat.display }} (https://{{ domain }}/crates/{{ squat.package }})
9+
{% endfor %}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
crates.io: Possible typosquatting in new crate "{{ crate_name }}"

src/worker/jobs/typosquat.rs

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ use diesel_async::AsyncPgConnection;
55
use typomania::Package;
66

77
use crate::Emails;
8-
use crate::email::Email;
8+
use crate::email::EmailMessage;
99
use crate::typosquat::{Cache, Crate};
1010
use crate::worker::Environment;
11+
use anyhow::Context;
12+
use minijinja::context;
13+
use tracing::{error, info};
1114

1215
/// A job to check the name of a newly published crate against the most popular crates to see if
1316
/// the new crate might be typosquatting an existing, popular crate.
@@ -55,14 +58,25 @@ async fn check(
5558
// hopefully care to check into things more closely.
5659
info!(?squats, "Found potential typosquatting");
5760

58-
let email = PossibleTyposquatEmail {
59-
domain: &emails.domain,
60-
crate_name: name,
61-
squats: &squats,
61+
let squats_data: Vec<_> = squats
62+
.iter()
63+
.map(|squat| {
64+
context! {
65+
display => squat.to_string(),
66+
package => squat.package()
67+
}
68+
})
69+
.collect();
70+
71+
let email_context = context! {
72+
domain => emails.domain,
73+
crate_name => name,
74+
squats => squats_data
6275
};
6376

6477
for recipient in cache.iter_emails() {
65-
if let Err(error) = emails.send(recipient, email.clone()).await {
78+
if let Err(error) = send_notification_email(emails, recipient, &email_context).await
79+
{
6680
error!(
6781
?error,
6882
?recipient,
@@ -76,45 +90,18 @@ async fn check(
7690
Ok(())
7791
}
7892

79-
#[derive(Debug, Clone)]
80-
struct PossibleTyposquatEmail<'a> {
81-
domain: &'a str,
82-
crate_name: &'a str,
83-
squats: &'a [typomania::checks::Squat],
84-
}
85-
86-
impl Email for PossibleTyposquatEmail<'_> {
87-
fn subject(&self) -> String {
88-
format!(
89-
"crates.io: Possible typosquatting in new crate \"{}\"",
90-
self.crate_name
91-
)
92-
}
93-
94-
fn body(&self) -> String {
95-
let squats = self
96-
.squats
97-
.iter()
98-
.map(|squat| {
99-
let domain = self.domain;
100-
let crate_name = squat.package();
101-
format!("- {squat} (https://{domain}/crates/{crate_name})\n")
102-
})
103-
.collect::<Vec<_>>()
104-
.join("");
105-
106-
format!(
107-
"New crate {crate_name} may be typosquatting one or more other crates.
108-
109-
Visit https://{domain}/crates/{crate_name} to see the offending crate.
110-
111-
Specific squat checks that triggered:
93+
async fn send_notification_email(
94+
emails: &Emails,
95+
recipient: &str,
96+
context: &minijinja::Value,
97+
) -> anyhow::Result<()> {
98+
let email = EmailMessage::from_template("possible_typosquat", context)
99+
.context("Failed to render email template")?;
112100

113-
{squats}",
114-
domain = self.domain,
115-
crate_name = self.crate_name,
116-
)
117-
}
101+
emails
102+
.send(recipient, email)
103+
.await
104+
.context("Failed to send email")
118105
}
119106

120107
#[cfg(test)]

0 commit comments

Comments
 (0)