@@ -5,9 +5,12 @@ use diesel_async::AsyncPgConnection;
55use typomania:: Package ;
66
77use crate :: Emails ;
8- use crate :: email:: Email ;
8+ use crate :: email:: EmailMessage ;
99use crate :: typosquat:: { Cache , Crate } ;
1010use 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